Подтвердить что ты не робот

Показатель Julia (Julia-lang) по сравнению с Fortran и Python

Я адаптировал простую программу для вычисления и построения вихрей перемещения для Julia для проверки языка, я также написал его на Python без особых причин.

(Отказ от ответственности: 1. Каждое сравнение производительности в stackoverflow, которое я прочитал, захлопнулось за то, что оно не было полным/правильным/хорошо написанным/релевантным и т.д. и т.д. - я не притворяюсь, что это реальное сравнение, я бы просто как узнать, как сделать Julia быстрее. 2. Я знаю, что python может быть оптимизирован, реализован в Cython и т.д., который не является частью этого обсуждения, он просто здесь для ссылки на эквивалентные функции в Julia и Python.)

Код и результаты работы можно увидеть в сущности.

Производительность Julia значительно медленнее Fortran. Время, затраченное на выполнение самого расчета, составляет (50000 временных шагов):

Fortran: 0.051s
Julia: 2.256s
Python: 30.846s

Юлия намного медленнее (~ 44 раза медленнее), чем Fortran, разрыв сужается, но по-прежнему значителен с 10-кратным шагом времени (0.50s vs 15.24s).

Эти результаты существенно отличаются от результатов, представленных на домашней странице julia. Что я делаю не так? Могу ли я исправить Юлию значительно быстрее?

Я прочитал страницу Julia Performance Tips и код сравнения на домашней странице Julia, и мне ничего не стоит исправлять.

Также интересно, что Julia очень медленно загружает PyPlot (5secs ish!!) и намного медленнее, чем Python, чтобы читать текстовый файл. Могу ли я сделать что-нибудь, чтобы улучшить эти вещи?

Обратите внимание, что приведенные выше времена не показывают время загрузки для Julia и Python, это просто время, затраченное на вычисление AFAIK - см. код. Для фортран это все. Графическое изображение было отключено, грубо, в каждом случае, чтобы обеспечить сравнение скорости.

Компьютер: Intel i7-3770, 16GB RAM, SSD HD, ОС: Ubuntu 13.10 64bit., Fortran: gfortran, GNU Fortran (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1, Julia: версия 0.3.0- preerelease + 396 (2013-12-12 00:18 UTC), Commit c5364db * (мастер 0 дней), x86_64-linux-gnu, Python: 2.7.5 +


Обновление:

Основываясь на совете ivarne, я переписал Julia script (обновленный в gist выше): инкапсулируя работу grunt в функции, объявляя тип всего и разбивая различные элементы матриц на разные массивы, где это применимо. (Я включил Float64 в довольно нескольких местах, так как я попытался Float32, чтобы увидеть, помогло ли это, это было не в большинстве случаев).

Результаты следующие:

50,000 временные шаги:

Fortran: 0.051s (entire programme)
Julia: raw calc.: 0.201s, calc. and return (?): 0.758s, total exec.: 6.947s

500,000 временные шаги:

Fortran: 0.495s (entire programme)
Julia: raw calc.: 1.547s, calc. and return (?): 2.094s, total exec.: 8.521s

В заключение:

  • Вы можете немного ускорить Джулию.

  • Вы можете существенно повлиять на кажущуюся скорость Julia в зависимости от того, как вы измеряете ее производительность.

4b9b3361

Ответ 1

Я уже некоторое время следил за проектом Julia, и у меня есть некоторые комментарии к соответствующему коду.

  • Кажется, что вы используете значительную часть кода в глобальной области. Глобальная среда в настоящее время очень медленная в Julia, поскольку типы всех переменных должны быть проверены на каждой итерации. Петли обычно должны быть записаны в функции.
  • Кажется, вы используете нарезку массива. В настоящее время это делает копию, потому что у Джулии нет быстрых представлений Array. Вы можете попытаться переключить их для subarray, но в настоящее время они намного медленнее, чем должны.

Время загрузки PyPlot (и любого другого пакета) является известной проблемой и связано с тем, что синтаксический анализ и компиляция кода Julia на машинный код занимает много времени. Есть идеи о том, что для этого процесса есть кеш, чтобы этот процесс стал мгновенным, но он еще не закончен. Базовая библиотека в настоящее время кэшируется в скомпилированном состоянии, поэтому большая часть инфраструктуры теперь находится на главной ветке.

ADDED: Я попытался запустить тест в изолированной функции и получил эти результаты. Смотрите gist

Синтаксический:

elapsed time: 0.334042578 seconds (11797548 bytes allocated)

И дерево последовательных рун основного тестового цикла.

elapsed time: 0.62999287 seconds (195210884 bytes allocated)
elapsed time: 0.39398753 seconds (184735016 bytes allocated)
elapsed time: 0.392036875 seconds (184735016 bytes allocated)

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

Обновление 2 С некоторой улучшенной обработкой памяти (обеспечить повторное использование массивов, поскольку присваивание не копируется), я получил время до 0,2 секунды (на моей машине). Существует определенно больше, что можно было бы сделать, чтобы не выделять новые массивы, но потом это начинает немного сложнее.

Эта строка не делает то, что вы думаете:

vx_old = vx

но это делает то, что вы хотите:

copy!(vx_old, vx)

и детекционировать один цикл.

x += 0.5*(vx + vx_old)*delta_t
y += 0.5*(vy + vy_old)*delta_t

в

for i = 1:nvortex
    x[i] += 0.5*(vx[i] + vx_old[i])*delta_t
    y[i] += 0.5*(vy[i] + vy_old[i])*delta_t
end

Ответ 2

@ivarne охватывает это, но он уделяет немного больше внимания:

julia> @time x=[1:10000];
elapsed time: 1.544e-5 seconds (80120 bytes allocated)

julia> @time y = x[1:10000];
elapsed time: 2.6857e-5 seconds (80120 bytes allocated)

Ого. Это много времени и памяти.

julia> @time z = sub(x,1:10000);
elapsed time: 6.239e-6 seconds (296 bytes allocated)

Гораздо лучше. Почему [:] не выполняет sub? Я не знаю. Ну, я вроде как делаю. Когда вы переходите к индексу z[10], думает Джулия, hrmm, z как x, за исключением того, что индексы смещены на 0, поэтому z[10] - x[10+0]. Вот так. Это небольшое дополнительное дополнение будет стоить вам в долгосрочной перспективе, если вы сделаете много индексирования. Чтобы исправить это, вам нужна концепция, подобная указателям, которая противоречит религии Джулии.

Обновить Джулия теперь осуждает [:] (версия 0.4.0)

julia> @time x=[1:10000];
WARNING: [a] concatenation is deprecated; use collect(a) instead
in depwarn at deprecated.jl:73
in oldstyle_vcat_warning at ./abstractarray.jl:29
in vect at abstractarray.jl:32
while loading no file, in expression starting on line 155
0.530051 seconds (180.12 k allocations: 9.429 MB, 5.26% gc time)

julia> @time x=[1:10000];
WARNING: [a] concatenation is deprecated; use collect(a) instead
in depwarn at deprecated.jl:73
in oldstyle_vcat_warning at ./abstractarray.jl:29
in vect at abstractarray.jl:32
while loading no file, in expression starting on line 155
0.001373 seconds (303 allocations: 714.656 KB)

собирать быстрее

julia> @ time x=collect(1:10000);
0.003991 seconds (35 allocations: 80.078 KB)

julia> @ time x=collect(1:10000);
0.000031 seconds (8 allocations: 78.406 KB)

сравнимый с SubArrays

julia> @time z = sub(x,1:10000);
0.067002 seconds (36.27 k allocations: 1.792 MB)

julia> @time z = sub(x,1:10000);
0.000016 seconds (7 allocations: 288 bytes)