Самый быстрый язык для циклов FOR

Я пытаюсь найти лучший язык программирования для аналитической модели, которую я создаю. Основным соображением является скорость, с которой он будет работать для циклов.

подробно:

  • модель должна выполнять многочисленные (~30 на запись, более 12 циклов) операции над набором элементов из массива-в массиве есть ~300k строк и ~150 столбцов. Большинство этих операций логичны по своей природе, например, если place (i) = 1, то j (i) = 2.
  • Я построил более раннюю версию этой модели с помощью Octave -- для запуска требуется ~55 часов на Amazon EC2 m2.экземпляр xlarge (и он использует ~10 ГБ памяти, но я совершенно счастлив бросить на него больше памяти). Octave / Matlab не будет выполнять элементарные логические операции, поэтому необходимо большое количество циклов for-я относительно уверен, что я векторизовал как можно больше-циклы, которые остаются необходимыми. Я получил octave-multicore для работы с этим кодом, что делает некоторые улучшение (~30% снижение скорости, когда я запускаю его на 8 ядрах EC2), но в конечном итоге неустойчиво с блокировкой файлов и т. д. +Я действительно ищу изменение шага во время выполнения - я знаю, что на самом деле использование Matlab может дать мне 50%-ное улучшение от просмотра некоторых тестов, но это непомерно дорого. Первоначальный план при запуске этого состоял в том, чтобы запустить Монте-Карло с этим, но в 55 часов пробега это совершенно непрактично.
  • следующая версия этого будет полная перестройка с нуля (по причинам IP я не войду, если ничего другого), поэтому я полностью открыт для любого языка программирования. Я больше всего знаком с Octave / Matlab, но баловался R, C, C++, Java. Я также владею w / SQL, если решение включает хранение данных в базе данных. Я выучу любой язык для этого-это не сложная функциональность, которую мы ищем, никакого взаимодействия с другими программами и т. д., поэтому не слишком обеспокоены изучением кривая.

Итак, со всем сказанным, какой самый быстрый язык программирования специально для циклов? от поиска SO и Google, Fortran и C bubble до вершины, но ищете еще несколько советов, прежде чем нырять в тот или иной.

спасибо!

14 ответов


с точки зрения абсолютной скорости, вероятно, Fortran, а затем C, а затем c++. В практическом применении хорошо написанный код в любом из трех, скомпилированный с помощью компилятора descent, должен быть достаточно быстрым.

Edit-как правило, вы увидите гораздо лучшую производительность с любым циклическим или разветвленным (например, операторы if) кодом со скомпилированным языком по сравнению с интерпретируемым языком.

чтобы привести пример, в недавнем проекте, над которым я работал, размеры данных и операции были примерно на 3/4 размера того, о чем вы говорите здесь, но, как и ваш код, имели очень мало места для векторизации и требовали значительного цикла. После преобразования кода из matlab в C++ время выполнения увеличилось с 16-18 часов до примерно 25 минут.


это для цикла выглядит не сложнее, чем это, когда он попадает в CPU:

for(int i = 0; i != 1024; i++) переводится как

mov r0, 0           ;;start the counter    
top:

;;some processing

add r0, r0, 1       ;;increment the counter by 1
jne top: r0, 1024   ;;jump to the loop top if we havn't hit the top of the for loop (1024 elements)

;;continue on

как вы можете сказать, это достаточно просто, вы не можете оптимизировать его очень хорошо[1]... Переориентируйтесь на уровень алгоритма.

первый разрез в проблеме-посмотреть на локальность кэша. Посмотрите классический пример умножения матрицы и замены i и j индексы.

edit: как второй разрез, я бы предложите оценить алгоритм для зависимостей данных между итерациями и зависимостей данных между локальностями в вашей "матрице" данных. Это может быть хорошим кандидатом для распараллеливания.

[1] Есть несколько микро-оптимизация возможна, но они не будут производить speedsups, которые вы ищете.


~300k * ~150 * ~30 * ~12 = ~16G итераций, верно? Это количество примитивных операций должно завершиться в течение нескольких минут (если не секунд) на любом скомпилированном языке на любом приличном процессоре. Fortran, C / C++ должны делать это почти одинаково хорошо. Даже языков, таких как Java и C#, только отстают с небольшим отрывом (если вообще).

Если у вас есть проблема с итерациями ~16G, выполняющимися 55 часов, это означает, что они очень далеки от примитива (80k в секунду? это смешно), так что может, нам стоит узнать больше. (как уже было предложено, ограничивает ли доступ к диску производительность? это доступ к сети?)


Как сказал @Rotsor, 16G operations / 55 hours-это около 80 000 операций в секунду, или одна операция каждые 12,5 микросекунды. Это много времени на операцию.

это означает, что ваши циклы не являются причиной низкой производительности, это то, что на внутренняя петля, которая занимает время. А Октава-это интерпретируемый язык. Одно это означает замедление на порядок.

Если вы хотите скорость, вам сначала нужно быть в скомпилированном языке. Затем вам нужно выполнить настройку производительности (он же профилирование) или просто один шаг в отладчике на уровне инструкций. Что скажет вам, что это на самом деле делает в своем сердце. Как только вы получите его туда, где он не тратит циклы, более причудливое оборудование, ядра, CUDA и т. д. даст вам ускорение параллелизма. Но глупо делать это, если ваш код занимает излишне много циклов. (вот пример настройки производительности-ускорение 43x только путем обрезки жир.)

Я не могу поверить, что количество респондентов говорят о matlab, APL и других векторизованных языках. Это переводчики. Они дают вам краткий исходный код, не на всех то же самое, что и fast исполнение. Когда дело доходит до голого металла, они застряли с тем же оборудованием, как и любой другой язык.


Added: чтобы показать вам, что я имею в виду, я просто запустил этот код C++, который делает 16G операции, на этом заплесневелом старом ноутбуке, и это заняло 94 секунды, или около 6ns за итерацию. (Я не могу поверить, что ты сидел с этой штукой целых 2 дня.)

void doit(){
  double sum = 0;
  for (int i = 0; i < 1000; i++){
    for (int j = 0; j < 16000000; j++){
      sum += j * 3.1415926;
    }
  }
}

для того, что вы обсуждаете, Fortran, вероятно, ваш первый выбор. Ближайшее второе место -наверное C++. Некоторые библиотеки C++ используют "шаблоны выражений", чтобы получить некоторую скорость над C для такого рода задач. Не совсем уверен, что это принесет вам пользу, но C++ может быть по крайней мере так же быстр, как C, и, возможно, несколько быстрее.

по крайней мере, теоретически, нет причин, по которым ада не могла бы быть конкурентоспособной, но я так давно не использовал ее ни для чего как это, что я не решаюсь рекомендовать его-не потому, что это не хорошо, но потому, что я просто не отслеживал текущие компиляторы Ada достаточно хорошо, чтобы комментировать их разумно.


любой скомпилированный язык должен выполнять сам цикл примерно на равных условиях.

Если вы можете сформулировать свою проблему в ее терминах, вы можете посмотреть на CUDA или OpenCL и запустить свой матричный код на GPU - хотя это менее хорошо для кода с большим количеством условных обозначений.

Если вы хотите остаться на обычных процессорах, вы можете сформулировать свою проблему с точки зрения операций SSE scatter/gather и bitmask.


наверное, язык ассемблера для любой вашей платформы. Но компиляторы (особенно специальные, которые специально нацелены на одну платформу (например, аналоговые устройства или Ti DSPs)) часто так же хороши, как и люди, или лучше. Кроме того, компиляторы часто знают о трюках, которые вы не знаете. Например, вышеупомянутые DSPs поддерживают циклы нулевых накладных расходов, и компилятор будет знать, как оптимизировать код для использования этих циклов.


Matlab будет выполнять логические операции по элементам, и они, как правило, довольно быстры.

вот краткий пример на моем компьютере (AMD Athalon 2.3 GHz w / 3GB):

d=rand(300000,150);
d=floor(d*10);

>> numel(d(d==1))
ans =
     4501524

>> tic;d(d==1)=10;toc;
Elapsed time is 0.754711 seconds.

>> numel(d(d==1))
ans =
     0
>> numel(d(d==10))
ans =
     4501524

В общем, я обнаружил, что операторы matlab очень быстры, вам просто нужно найти способы выразить свои алгоритмы непосредственно в терминах матричных операторов.


Как хранятся данные? Ваше время выполнения, вероятно, больше зависит от ввода-вывода (особенно диска или хуже, сети), чем от вашего языка.

предполагая, что операции над строками ортогональны, я бы пошел с C# и использовал PLINQ чтобы использовать весь параллелизм, который я мог.


не могли бы вы быть лучше с ручной кодированной ассемблерной вставкой? Если, конечно, вам не нужна переносимость.

Это и оптимизированный алгоритм должны помочь (и, возможно, реструктуризация данных?).

вы также можете попробовать несколько алгоритмов и профилировать их.


APL.

хотя он интерпретируется, его примитивные операторы все работают на массивах изначально, поэтому вам редко нужны явные циклы. Вы пишете один и тот же код, будь то скалярные данные или массив, и интерпретатор заботится о любом цикле, необходимом внутри, и, следовательно, с минимальными накладными расходами - сами циклы находятся на скомпилированном языке и будут сильно оптимизированы для конкретной архитектуры процессора, на котором он работает.

вот пример простоты обработки массива в APL:

    A <- 2 3 4 5 6 8 10
    ((2|A)/A) <- 0
    A
2 0 4 0 6 8 10

в первой строке задается вектор чисел. Вторая строка заменяет все нечетные числа в векторе с нуля. Третья строка запрашивает новые значения A, а четвертая строка-результирующий вывод.

обратите внимание, что явный цикл не требуется, так как скалярные операторы, такие как '|' (остаток), автоматически распространяются на массивы по мере необходимости. APL также имеет встроенные примитивы для поиска и сортировки, что, вероятно, будет быстрее, чем писать собственные циклы для этих операций.

статейка хорошая статья на APL, который также предоставляет ссылки на поставщиков, таких как IBM и Dyalog.


любой современный скомпилированный или JITted язык будет отображаться до почти того же кода машинного языка, давая накладные расходы цикла 10 nano секунды или меньше, за итерацию, на современных процессорах.

Цитирую @Rotsor:

Если у вас есть проблема с итерациями ~16G, выполняющимися 55 часов, это означает, что они очень далеки от примитива (80k в секунду? это смешно), поэтому, возможно, мы должны знать больше.

80k операции в секунду составляют около 12,5 микросекунд каждый-коэффициент 1000 больше, чем накладные расходы цикла, которые вы ожидаете.

предполагая, что ваша 55-часовая среда выполнения однопоточна, и если ваши операции с каждым элементом так же просты, как предложено, вы должны быть в состоянии (консервативно) достичь ускорения 100x и сократить его до менее чем за час очень легко.

Если вы хотите работать еще быстрее, вы хотите посмотреть на написание многопоточного решения, в этом случае язык, который необходима хорошая поддержка. Я бы склонялся к PLINQ и C# 4.0, но это потому, что я уже знаю C# - YMMV.


C++ - это не быстро, когда делаешь matrixy вещи для петель. C, на самом деле, особенно плохо в этом. См.хорошая математика плохая математика.

Я слышал, что C99 имеет _ _ restrict указатели, которые помогают, но не знаю много об этом.

Fortran по-прежнему является языком goto для численных вычислений.


Как насчет ленивого языка загрузки, такого как clojure. это lisp, так как большинство диалектов lisp не имеет цикла for, но имеет много других форм, которые работают более идиоматически для обработки списка. Это также может помочь в масштабировании, поскольку операции потокобезопасны и поскольку язык функционален, он имеет меньше побочных эффектов. Если вы хотите найти все элементы в списке, которые были значениями "i", чтобы работать с ними, вы можете сделать что-то вроде этот.

(def mylist ["i" "j" "i" "i" "j" "i"])
(map #(= "i" %) mylist)

результат

(true false true true false true)