Я разрабатываю неинтерактивное приложение с привязкой к процессору, которое выполняет только вычисления, почти без ввода-вывода. В настоящее время он работает слишком долго, и пока я работаю над улучшением алгоритма, я также думаю, может ли он дать какую-либо пользу от смены языка или платформы. В настоящее время это C++ (без ООП, поэтому это почти C) для Windows, скомпилированный с помощью компилятора Intel C++. Может ли переход на ASM помочь и насколько? Может ли помочь переход на Linux и GCC?





Переход на ASM не сильно поможет, если только вы не очень хорошо в этом разбираетесь и / или не имеете определенной процедуры критического пути, которую, как вы знаете, вы можете сделать лучше. Как отметили несколько человек, современные компиляторы в большинстве случаев лучше используют преимущества кеширования и т. д. чем кто-либо может сделать вручную.
Я бы посоветовал:
C++ должен обеспечивать максимально возможную производительность кода, поэтому я бы не рекомендовал переключать язык. В зависимости от приложения вы можете повысить производительность в системах с несколькими кодами и процессорами, используя несколько потоков, в качестве еще одного предложения.
Хотя простое переключение на asm не даст никаких преимуществ, поскольку компилятор Intel C++, вероятно, лучше оптимизирует, чем вы, вы можете попробовать один из следующих вариантов:
Обновлено: я также поддерживаю профильный подход. Я рекомендую AQTime, который поддерживает компилятор Intel C++.
Лично я бы посмотрел на языки, которые позволяют легче всего использовать преимущества параллелизма, если только это не полностью непараллелизируемая ситуация. Возможность установить несколько дополнительных ядер и получить (если возможно!) Почти линейное улучшение может быть намного более рентабельным, чем выжимание лишних нескольких процентов эффективности.
Когда дело доходит до распараллеливания, я считаю, что функциональные языки часто рассматриваются как лучший вариант, или вы могли бы взглянуть на OpenMP для C / C++. (Лично я, как специалист по управляемому языку, искал бы библиотеки для Java / .NET, но я прекрасно понимаю, что не у всех одинаковые предпочтения!)
Это всегда алгоритм, реже язык. Вот моя подсказка: «пока я работаю над улучшением алгоритма».
Настройка может оказаться недостаточной.
Рассмотрим радикальные изменения в алгоритме. Вы должны исключить обработку, а не ускорить ее выполнение. Виной всему часто бывает «поиск» - просмотр данных в поисках чего-либо. Найдите способы избавиться от поиска. Если вы не можете его устранить, замените линейный поиск каким-либо поиском по дереву или какой-либо хеш-картой.
Я не согласен. Это алгоритм и оборудование. Даже после того, как вы доведете алгоритм до минимума, вы можете платить огромные накладные расходы на ЦП, если не поиграете с организацией вещей (например, с кешированием и разбиением по страницам).
да. Это всегда алгоритм. Некоторые языки могут выражать то, что другие не могут. Некоторые могут выражать вещи легче, чем другие языки. Некоторые задачи в C++ проще, некоторые сложнее.
@Aaron: на техническом уровне почти все языки одинаковы - все они Turing Complete. Однако некоторые вещи удобнее на одном языке. Алгоритмы всегда не зависят от языка.
@Uri: Большинство алгоритмов проблемы компромисс между памятью и временем. Если вам нужно меньше времени, вам часто приходится использовать больше памяти. Кэш никогда не является проблемой. Однако проблемы с подкачкой означают неразумный баланс между временем и памятью. Не аппаратная проблема. Проблема алгоритма.
Совершенно неправильно. Это действительно алгоритм плюс оборудование. У процессоров есть проблемы с кэшированием, конвейером и ветвлением, что означает, что один и тот же алгоритм может работать медленнее, чем другой на кристалле, и тем не менее вы можете получить противоположные результаты на другом микропроцессоре.
Примеры: Джефф Этвуд однажды заметил, что gzip на Athlons работает намного медленнее, чем на Core Duos, хотя «GZip - это просто Deflate плюс контрольная сумма и верхний / нижний колонтитулы». В списке рассылки Mersenne Prime Search было отмечено, что Lucas-Lehmer Prime95 лучше работает на Pentium, чем на AMD, из-за штрафа за ветвь.
@ Джо Пинеда: Извините, плохой алгоритм никогда не будет работать на процессоре Amy. Правильный алгоритм может работать лучше на Intel, чем на Sparc, но неправильный алгоритм везде будет ужасен.
Я имею в виду, что у вас может быть 2 функционально эквивалентных алгоритма хорошо, но один может работать на процессоре быстрее, чем другой, обратный - на другом процессе. Если вы ограничены процессором, вам необходимо профиль - анализ алгоритмов сам по себе не заменяет этого и не является достаточным, никогда.
Анекдот: однажды я нашел запрос ужасный в хранимой процедуре. Реорганизовал его, пока план выполнения не стал значительно короче. Протестировал и обнаружил, что в производстве помедленнее больше, чем в предыдущей версии. Почему? Оказалось, что SQL S. запускает старую версию параллельно, но не может этого сделать с новой.
@ Джо Пинеда: Профилирование - это хорошо. Тонкая настройка - пустая трата времени. Очень важно отказаться от плохого алгоритма и заменить его хорошим. Оборудование не имеет значения. Это не так важно, как фундаментальный дизайн структуры алгоритм-dsta.
В качестве альтернативного подхода вы можете изучить Распределенных вычислений, который звучит так, как будто он может удовлетворить ваши потребности.
Просто чтобы быть внимательным: первое, что нужно сделать, - это собрать данные профиля, а второе, что нужно сделать, - это рассмотреть свои алгоритмы. Я уверен, что вы это знаете, но они должны быть # включены в любое обсуждение программирования производительности.
Чтобы прямо сказать о вашем вопросе "Может ли помочь переход на ASM?" ответ: «Если вы не знаете ответа на этот вопрос, то, вероятно, нет». Если вы не очень хорошо знакомы с архитектурой ЦП и ее входами и выходами, маловероятно, что вы сделаете значительно лучшую работу, чем хороший оптимизирующий компилятор C / C++ для вашего кода.
Следующее, на что следует обратить внимание, это то, что значительное ускорение вашего кода (помимо алгоритмических улучшений) почти наверняка будет происходить за счет параллелизма, а не линейного увеличения. Настольные компьютеры теперь могут задействовать 4 или 8 ядер в задаче, что имеет гораздо больший потенциал производительности, чем немного лучший генератор кода. Поскольку вам комфортно с C / C++, OpenMP практически не вызывает затруднений; его очень легко использовать для распараллеливания ваших циклов (очевидно, вы должны следить за зависимостями, переносимыми циклами, но это определенно «простейший параллелизм, который мог бы работать»).
При этом качество генерации кода в разных компиляторах C / C++ различается. Компилятор Intel C++ хорошо известен своим качеством оптимизации и полностью поддерживает не только OpenMP, но и другие технологии, такие как Threading Building Blocks.
Переходя к вопросу о том, какие языки программирования могут быть даже лучше, чем C++, ответом будет «языки программирования, которые активно продвигают / упрощают концепции параллелизма и параллельного программирования». Эрланг - первоклассный язык в этом отношении, и сейчас он является «горячим» языком, и большинство людей, интересующихся программированием производительности, уделяют ему хоть какое-то внимание, поэтому, если вы хотите улучшить свои навыки в этой области, вы можете Хотите проверить его.
Попробуйте Fortran 77 - когда дело доходит до вычислений, все равно ничто не сравнится с дедушкой языков программирования. Кроме того, попробуйте использовать OpenMP, чтобы использовать преимущества нескольких ядер.
В это сложно поверить, но это правда. (Я, кстати, заядлый программист на C++.) Основная причина - более жесткие правила псевдонимов в Fortran - в C / C++ компилятор не гарантирует, что указатель является единственным указателем, указывающим на данный фрагмент данных, что делает невозможными определенные оптимизации.
Иногда вы можете найти библиотеки, в которых есть оптимизированные реализации интересующих вас алгоритмов. Часто они делали за вас многопоточность.
Например, переключение с LINPACK на LAPACK дало нам 10-кратное увеличение скорости факторизации / решения LU с хорошей библиотекой BLAS.
Во-первых, выясните, можете ли вы изменить алгоритм, как предложил С.Лотт.
Предполагая, что выбор алгоритма правильный, вы можете посмотреть шаблоны доступа к памяти, если у вас есть много данных, которые вы обрабатываете. В наши дни многие приложения для обработки чисел привязаны к шине памяти, а не к ALU. Недавно я оптимизировал код, который имел форму:
// Assume N is a big number
for (int i=0; i<N; i++) {
myArray[i] = dosomething(i);
}
for (int i=0; i<N; i++) {
myArray[i] = somethingElse(myArray[i]);
}
...
и преобразовал его так:
for (int i=0; i<N; i++) {
double tmp = dosomething(i);
tmp = somethingElse(tmp);
...
myArray[i] = tmp;
}
...
В данном конкретном случае это дало примерно двукратное ускорение.
Как сказал Лобрайен, вы не предоставили нам никакой информации, чтобы сказать, поможет ли вручную оптимизированный код ASM ... что означает, что ответ, вероятно, «еще нет».
Вы запускали свой код с помощью профилировщика?
Вы знаете, работает ли код медленно из-за ограничений памяти или ограничений процессора?
Вы используете все доступные ядра?
Вы определили какие-либо используемые вами алгоритмы, отличные от O (1)? Сможете ли вы доставить их до O (1)? Если нет, то почему?
Если вы все это сделали, насколько вы контролируете среду, в которой работает ваша программа? (вероятно, много, если вы думаете о смене операционных систем). Можете ли вы отключить другие процессы, дать вашему процессу наивысший приоритет и т. д.? Как насчет того, чтобы просто найти машину с более быстрым процессором, большим количеством ядер или больше памяти (в зависимости от того, что у вас есть)
И так далее.
Если вы уже сделали все это и многое другое, вполне возможно, что вы дойдете до того момента, когда подумаете: «Интересно, можно ли было бы оптимизировать эти несколько строк кода прямо здесь лучше, чем сборка, на которую я смотрю? отладчик прямо сейчас? " И тут вы можете спросить конкретно.
Удачи! Вы решаете проблему, которую интересно решать.
Если вы придерживаетесь C++ в компиляторе Intel, взгляните на встроенные функции компилятора (полный справочник здесь). Я знаю, что в VC++ есть аналогичная функциональность, и я уверен, что вы можете сделать то же самое с gcc. Это может позволить вам в полной мере использовать параллелизм, встроенный в ваш процессор. Вы можете использовать инструкции MMX, SSE и SSE2 для некоторого повышения производительности. Как уже говорили другие, вам, вероятно, лучше сначала взглянуть на алгоритм.
Я предлагаю вам переосмыслить свой алгоритм или, может быть, даже лучше, свой подход. С другой стороны, возможно, то, что вы пытаетесь вычислить, просто требует много вычислительного времени. Вы думали о том, чтобы сделать его распределенным, чтобы он мог работать в каком-то кластере? Если вы хотите сосредоточиться на оптимизации чистого кода, представив Assembler для вашего внутренние петли, то часто это может быть очень полезно (если вы знаете, что делаете).
Ручная оптимизация кода ASM по сравнению с тем, что может сделать для вас C++, редко бывает рентабельной.
Если вы сделали все, что могли, с алгоритмом из традиционного алгоритмического представления, и вы также устранили излишки, то вы можете быть либо SOL, либо вы можете оптимизировать свою программу с точки зрения оборудования..
Например, каждый раз, когда вы следуете за указателем вокруг кучи, вы платите огромные деньги из-за промахов в кеше, возможно, подкачки и т. д., Которые все влияют на предсказания ветвления. Большинство программистов (даже гуру C) склонны смотреть на процессор с функциональной точки зрения, а не на то, что происходит за кулисами. Иногда реорганизация памяти, например путем «выравнивания» или выделения памяти вручную для размещения на одной странице, может привести к ОГРОМНОМУ ускорению. Мне удалось получить двукратное ускорение при обходе графов, просто сгладив мои структуры.
Это не то, что ваш компилятор сделает за вас, поскольку они основаны на вашем высокоуровневом понимании программы.
Для современных процессоров изучение ASM займет у вас много времени. Кроме того, со всеми различными версиями SSE ваш код в конечном итоге будет сильно зависеть от процессора.
Я выполняю довольно много работы, связанной с процессором, и обнаружил, что разница между компилятором Intel C++ и g ++ обычно не такая большая (не более 15% или около того), и нет измеримой разницы между Mac OS X, Windows и Linux.
Вам придется вручную оптимизировать код и улучшать алгоритм. Боюсь, что не существует «волшебной волшебной пыли», которая могла бы сделать существующий код намного быстрее.
Если вы еще этого не сделали и заботитесь о производительности, вы ДОЛЖНЫ запустить свой код через хороший профилировщик (лично мне нравятся kcachegrind & valgrind в Linux или Shark в Mac OS X. Я не знаю, что хорошо для Windows Боюсь).
Исходя из моего прошлого опыта, есть очень большая вероятность, что вы обнаружите, что какой-то метод отнимает 95% вашего процессорного времени, а некоторые простые изменения или добавление кеширования значительно улучшат вашу производительность. Аналогичным образом, если какой-либо метод занимает всего 1% вашего процессорного времени, никакая оптимизация ничего вам не даст.
Два очевидных ответа на вопрос "ограничено ЦП": 1. Используйте больше ЦП (ядер). 2. Используйте что-нибудь другое.
Использование 2 потоков вместо 1 сократит затраченное время до 50%. Для сравнения, C++ и ASM редко дают вам 5% (а для начинающих программистов ASM это часто -5%!). Некоторые проблемы хорошо масштабируются и могут выиграть от 8 или 16 ядер. Такое оборудование по-прежнему широко распространено, так что посмотрите, попадают ли ваши проблемы в эту категорию.
Другое решение - использовать для этой задачи более специализированное оборудование. Это может быть векторная единица вашего процессора - учитывая, что Windows = x86 / x64, это будет разновидность SSE. Другой вид векторной техники - современные графические процессоры. Графический процессор также имеет собственную шину памяти, что довольно быстро.
Сначала вытащите поводок. Тогда, если это так быстро, как только возможно, без перехода в ASM, пусть будет так. Но мысль о том, что вам нужно перейти в ASM, предполагает, что вы знаете, что делает его медленным, и я готов поспорить, что вы догадываетесь.
Если вы чувствуете, что оптимизировали свой код до такой степени, что улучшения нет, увеличьте загрузку процессора. Это можно сделать на разных платформах. Я занимаюсь разработкой Appistry. Несколько ссылок:
http://www.appistry.com/resource-library/index.html
и вы можете бесплатно скачать продукт отсюда:
http://www.appistry.com/developers/
Я работаю в Appistry, и мы выполнили много установок для задач, которые были связаны с процессором, распределяя работу между десятками или сотнями машин.
Надеюсь это поможет, -Бретт
Как уже намекнул Oregonghost - компилятор VectorC может помочь. Однако на самом деле он не распараллеливает код, вместо этого вы можете использовать его для использования расширенных наборов команд, таких как mmx или sse. Я использовал его для наиболее критичных по времени частей в движке программного рендеринга, и это привело к ускорению примерно на 150-200% на большинстве процессоров.
Переход на Linux может помочь, если вы разделите его до тех частей, которые вам действительно нужны.
Трудно создать код ASM быстрее, чем наивный код C или C++. В большинстве случаев, если вы сделаете эту работу действительно хорошо, вы, вероятно, наберете не больше нескольких процентов, и получение примерно 10% ускорения считается большим успехом, но в большинстве случаев это просто невозможно.
Компиляторы способны понять, как эффективно компилировать. Вы должны профилировать, чтобы выяснить, где оптимизировать.
CrowdProcess имеет около 2000 воркеров, которые вы можете использовать для вычисления своего алгоритма. API чрезвычайно прост, и мы наблюдали ускорение, близкое к количеству рабочих. Также вы можете написать Javascript, который сделает вас более продуктивным, чем C++ или ASM.
Поэтому, если вы находитесь между C++ или ASM, я бы сказал, что вы должны сначала использовать все ядра процессора, а если этого недостаточно, CrowdProcess должен быть интересной платформой.
Отказ от ответственности: я создал CrowdProcess.
Да, в частности, я бы посмотрел на Clojure. Это язык JVM, созданный кем-то, кто кажется одержимым параллелизмом, поэтому он многое берет от функциональных языков (например, неизменяемых структур данных).