2014-04-21 5 views
4

Я пытаюсь узнать немного Джулии, после прочтения этого руководства в течение нескольких часов, я написал следующий фрагмент кода:Как улучшить производительность этой части кода?

ie = 200; 
ez = zeros(ie + 1); 
hy = zeros(ie); 

fdtd1d (steps)= 
    for n in 1:steps 
     for i in 2:ie 
      ez[i]+= (hy[i] - hy[i-1]) 
     end 
     ez[1]= sin(n/10) 
     for i in 1:ie 
      hy[i]+= (ez[i+1]- ez[i]) 
     end 
    end 

@time fdtd1d(10000); 
elapsed time: 2.283153795 seconds (239659044 bytes allocated) 

Я считаю, что это при оптимизации, так как это гораздо медленнее, чем соответствующая Mathematica версии:

ie = 200; 
ez = ConstantArray[0., {ie + 1}]; 
hy = ConstantArray[0., {ie}]; 

fdtd1d = Compile[{{steps}}, 
    Module[{ie = ie, ez = ez, hy = hy}, 
    Do[ez[[2 ;; ie]] += (hy[[2 ;; ie]] - hy[[1 ;; ie - 1]]); 
    ez[[1]] = Sin[n/10]; 
    hy[[1 ;; ie]] += (ez[[2 ;; ie + 1]] - ez[[1 ;; ie]]), {n, 
     steps}]; [email protected]; [email protected]]]; 

result = fdtd1d[10000]; // AbsoluteTiming 
{0.1280000, Null} 

Итак, как сделать версию Julia fdtd1d быстрее?

ответ

5

Две вещи:

При первом запуске функции время будет включать время компиляции кода. Если вы хотите, чтобы яблоки сравнивали яблоки с скомпилированной функцией в Mathematica, вы должны запустить функцию дважды и время второго запуска. С вашего кода я получаю:

elapsed time: 1.156531976 seconds (447764964 bytes allocated) 

для первого запуска, который включает в себя время компиляции и

elapsed time: 1.135681299 seconds (447520048 bytes allocated) 

для второго запуска, когда вам не нужно компилировать.

Вторая вещь, и, возможно, большая вещь заключается в том, что вам следует избегать глобальных переменных в критическом для производительности коде. Это первый советник в the performance tips section of the manual.

Вот тот же код, используя локальные переменные:

function fdtd1d_local(steps, ie = 200) 
    ez = zeros(ie + 1); 
    hy = zeros(ie); 
    for n in 1:steps 
     for i in 2:ie 
      ez[i]+= (hy[i] - hy[i-1]) 
     end 
     ez[1]= sin(n/10) 
     for i in 1:ie 
      hy[i]+= (ez[i+1]- ez[i]) 
     end 
    end 
    return (ez, hy) 
end 

fdtd1d_local(10000) 
@time fdtd1d_local(10000); 

Для сравнения кода Mathematica на моей машине дает

{0.094005, Null} 

в то время как результат от @time для fdtd1d_local является:

elapsed time: 0.015188926 seconds (4176 bytes allocated) 

Или примерно в 6 раз быстрее. Глобальные переменные имеют большое значение.

+1

На моем компьютере это занимает 0.018759438 секунд. Ваш ответ сделал эту часть руководства намного яснее! – xzczd

+1

И хотя я ненавижу это признавать, но когда «шаги» больше, Джулия бьет _Mathematica_ ... для «steps = 10^7' _Mathematica_ занимает около 103 секунд, а Джулии - всего около 19 секунд. – xzczd

0

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

оригинальный код:

ie = 200; 
ez = zeros(ie + 1); 
hy = zeros(ie); 

fdtd1d (steps)= 
    for n in 1:steps 
     for i in 2:ie 
      ez[i]+= (hy[i] - hy[i-1]) 
     end 
     ez[1]= sin(n/10) 
     for i in 1:ie 
      hy[i]+= (ez[i+1]- ez[i]) 
     end 
    end 

@time fdtd1d(10000); 

Выход

julia> 
elapsed time: 1.845615295 seconds (239687888 bytes allocated) 

ОПТИМИЗИРОВАННАЯ КОД:

ie = 200; 
ez = zeros(ie + 1); 
hy = zeros(ie); 

fdtd1d (steps)= 
    for n in 1:steps 


     ez[2:ie] = ez[2:ie]+hy[2:ie]-hy[1:ie-1]; 
     ez[1]= sin(n/10); 
     hy[1:ie] = hy[1:ie]+ez[2:end]- ez[1:end-1] 

    end 

@time fdtd1d(10000); 

ВЫХОД

julia> 
elapsed time: 0.93926323 seconds (206977748 bytes allocated) 
+0

В моем компьютере ваш оптимизированный для кода код займет только 0.366747729 секунд. Что ж, странно, я думаю, что руководство предложило нам детекционировать выражения ... – xzczd

+4

Хотя этот подход - это то, как один из них идет о оптимизации кода на большинстве высокоуровневых числовых языков, это совершенно противоположно тому, что нужно делать в Джулии. Джон Майлс Уайт имеет отличную пост-сравнительную оценку и детекторизацию в R и Julia: http://www.johnmyleswhite.com/notebook/2013/12/22/the-relationship-between-vectorized-and-devectorized-code/. Настоящий виновник производительности здесь заключается в том, что данные находятся в непостоянных глобальных переменных. Векторизация помогает немного, потому что это уменьшает влияние переменных на глобальные, но правильное исправление заключается в том, чтобы обернуть это в функции let или function. – StefanKarpinski

Смежные вопросы