предисловие
Совсем недавно была официально запущена версия 1.0 языка Julia.Как артефакт научных и числовых вычислений, Julia привлекла широкое внимание в отрасли. Язык Julia известен своей скоростью, но его производительность также подвергалась сомнению еще до того, как вышла версия 1.0.Эта статья является ответом разработчика Тома Квонга на вопрошающий голос — почему критика «отказ от Джулии» является легкомысленной. В этой статье используется версия Julia0.6.
Составление: Переводческая группа Цзижи
источник:tk3369.wordpress.com
Оригинальное название: Обновленный анализ сообщения в блоге «Отказ от Джулии»
задний план
В сообщении в блоге в марте 2016 года я разглагольствовал о языке Julia. Судя по комментариям в этом блоге и недавним обсуждениям сообщества языка Julia, этот блог действительно вызывает споры.
адрес блога:
З вер ОВИ super.net/2016/05/13/…
Таким образом, цель этого сообщения в блоге — опровергнуть эти неправильные представления, а затем определить, на чем нам действительно нужно сосредоточиться. Я постараюсь быть максимально честным, чтобы перечислить различия, чтобы вы могли судить конкретно на примерах.
Все приведенные ниже характеристики основаны на моем ноутбуке Macbook Pro (процессор Intel i5-4258U, частота 2,40 ГГц) с Julia 0.6.2 и Python 3.6.3 из дистрибутива Anaconda.
Жалобы на низкоуровневое тестирование производительности Юлии
Автор ссылается на несколько вопросов github о тестировании производительности.
1.parse_integer benchmark(GitHub.com/Джулия Лэнг/ Просто…) не кодируется с использованием идиоматических стандартов, таких как функция strlen. Основная группа разработчиков признала наличие проблемы в октябре 2013 г. и быстро ее устранила (в течение двух дней).
2. Были также вопросы о различных тестах производительности, реализованных в Java и Octave. После нескольких раундов дискуссий на Github все наконец пришли к разным представлениям о тестировании производительности. Действительно ли мы хотим использовать тестирование производительности, чтобы понять производительность различных языковых реализаций одного и того же алгоритма, или мы хотим знать, как сравнить наиболее оптимизированный код между этими языками? Тест производительности Джулии объясняет:
Главное отметить, что код теста производительности не был написан с абсолютной максимальной производительностью (самый быстрый код для рекурсивного вычисления двадцатого элемента последовательности Фибоначчи — 6765). Вместо этого эти тесты производительности написаны для проверки производительности при реализации одних и тех же алгоритмов и шаблонов кода на каждом языке.
Об оптимизации
Можно оптимизировать что угодно и добиться хорошей производительности на любом расширяемом языке. В случае с Python мы можем применить Cython, Numba, Numpy и другие приемы. Это классическая двуязычная проблема, описанная Стефаном Карпински.
который:
1. Как сложно и как тяжело оптимизировать вещь.
2. Вносит ли оптимизация дополнительную сложность в код и снижает ли производительность.
Привет мир перформанс
Автор немного недоволен временем выполнения простой программы Hello World. Включение времени запуска, очевидно, странно и однобоко.
Вопрос в том, имеет ли это значение? Может есть, может нет.
1. Если у вас есть куча недолговечных приложений, которые запускаются всего за секунду, более длительное время запуска может иметь для вас большое значение.
2. Если у вас есть большой вычислительный проект, который занимает около 30 минут, время запуска в 0,5 секунды не имеет смысла. Это относительно обычная ситуация для большинства коммерческих приложений, не говоря уже о вычислительных и научных сообществах.
блог
«Как сделать Python таким же быстрым, как Джулия»
Автор указывает, что в блоге «Как сделать Python таким же быстрым, как Джулия» есть много способов сделать Python быстрее. Эта цитата как бы говорит: «Эй, мы, разработчики Python, уже знаем, как заставить программы работать быстрее, и нет причин переключаться на другой язык».
Тест производительности Фибоначчи
Результаты моих тестов показывают, что Cython-Typed работает так же быстро, как и Julia.
Всего несколько заметок
- Результаты теста производительности блога: Julia 80us и Cython-Typed 24us. Я не могу воспроизвести это поведение. Я могу только объяснить, что времена изменились, и показатели Юли улучшились.
- Функция Фибоначчи оптимизирована с использованием кэша LRU. С точки зрения тестирования производительности это некорректное сравнение, поскольку изменился сам алгоритм. Поэтому мы можем игнорировать эти результаты.
- Numba дополнительно оптимизирует код, используя LRU-Enhanced. По той же причине мы будем игнорировать и эти результаты.
Python
In [43]: def fib(n):
...: if n<2:
...: return n
...: return fib(n-1)+fib(n-2)
In [44]: %timeit fib(20)
3.27 ms ± 48.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Cython
In [48]: %%cython
...: def fib_cython(n):
...: if n<2:
...: return n
...: return fib_cython(n-1)+fib_cython(n-2)
In [51]: %timeit fib_cython(20)
1.48 ms ± 329 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Cython Typed
In [71]: %%cython
...: cpdef long fib_cython_type(long n):
...: if n<2:
...: return n
...: return fib_cython_type(n-1)+fib_cython_type(n-2)
In [72]: %timeit fib_cython_type(20)
47.8 µs ± 182 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each
Julia
julia> fib(n) = n < 2 ? n : fib(n-1) + fib(n-2)
fib (generic function with 1 method)
julia> @btime fib(20)
48.075 μs (0 allocations: 0 bytes)
быстрая сортировка
Автор преобразовал функцию быстрой сортировки в Cython и в итоге пришел к выводу, что время сортировки у Numpy самое быстрое.
1. Алгоритм тестирования производительности — Julia (356us) vs Cython (1030).
2. Базовая функция - Джулия (233us) против Numpy (292us).
Python
In [83]: def qsort1(a, lo, hi):
...: i = lo
...: j = hi
...: while i < hi:
...: pivot = a[(lo+hi) // 2]
...: while i <= j:
...: while a[i] < pivot:
...: i += 1
...: while a[j] > pivot:
...: j -= 1
...: if i <= j:
...: a[i], a[j] = a[j], a[i]
...: i += 1
...: j -= 1
...: if lo < j:
...: qsort1(a, lo, j)
...: lo = i
...: j = hi
...: return a
In [84]: %timeit qsort1(lst, 0, len(lst)-1)
13.7 ms ± 140 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Cython
In [100]: %%cython
...: cdef double[:] qsort2(double[:] a, long lo, long hi):
...: cdef:
...: long i, j
...: double pivot
...: i = lo
...: j = hi
...: while i < hi:
...: pivot = a[(lo+hi) // 2]
...: while i <= j:
...: while a[i] < pivot:
...: i += 1
...: while a[j] > pivot:
...: j -= 1
...: if i <= j:
...: a[i], a[j] = a[j], a[i]
...: i += 1
...: j -= 1
...: if lo < j:
...: qsort2(a, lo, j)
...: lo = i
...: j = hi
...: return a
...:
...: def qsort2_py(a, b, c):
...: return qsort2(a, b, c)
In [105]: %timeit qsort2_py(np.random.rand(5000), 0, 4999)
1.03 ms ± 110 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Numpy
In [61]: %timeit np.sort(lst)
292 µs ± 4.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Julia Microbenchmark
julia> function qsort!(a,lo,hi)
i, j = lo, hi
while i < hi
pivot = a[(lo+hi)>>>1]
while i <= j
while a[i] < pivot; i += 1; end while a[j] > pivot; j -= 1; end
if i <= j
a[i], a[j] = a[j], a[i]
i, j = i+1, j-1
end
end
if lo < j; qsort!(a,lo,j); end
lo, j = i, hi
end
return a
end
qsort! (generic function with 1 method)
julia> sortperf(n) = qsort!(rand(n), 1, n)
sortperf (generic function with 1 method)
julia> @btime sortperf(5000)
355.957 μs (2 allocations: 39.14 KiB)
Julia Base.sort
julia> @btime sort(rand(5000); alg=QuickSort)
233.293 μs (11 allocations: 78.66 KiB)
множество Мандельброта
Результаты моего теста показывают, что Numba (159us) против Julia (72us).
Numba
In [106]: @jit
...: def mandel_numba(z):
...: maxiter = 80
...: c = z
...: for n in range(maxiter):
...: if abs(z) > 2:
...: return n
...: z = z*z + c
...: return maxiter
In [107]: @jit
...: def mandelperf_numba_mesh():
...: width = 26
...: height = 21
...: r1 = np.linspace(-2.0, 0.5, width)
...: r2 = np.linspace(-1.0, 1.0, height)
...: mandel_set = np.empty((width,height), dtype=int)
...: for i in range(width):
...: for j in range(height):
...: mandel_set[i,j] = mandel_numba(r1[i] + 1j*r2[j])
...: return mandel_set
In [109]: %timeit mandelperf_numba_mesh()
:1: NumbaWarning: Function "mandelperf_numba_mesh" failed type inference: Invalid usage of Function() with parameters ((int64 x 2), dtype=Function(<class 'int'>))
* parameterized
File "", line 7
[1] During: resolving callee type: Function()
[2] During: typing of call at (7)
@jit
:1: NumbaWarning: Function "mandelperf_numba_mesh" failed type inference: cannot determine Numba type of <class 'numba.dispatcher.LiftedLoop'>
File "", line 8
@jit
:1: NumbaWarning: Function "mandelperf_numba_mesh" was compiled in object mode without forceobj=True, but has lifted loops.
@jit
159 µs ± 7.55 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Julia
julia> function mandel(z)
c = z
maxiter = 80
for n = 1:maxiter
if myabs2(z) > 4
return n-1
end
z = z^2 + c
end
return maxiter
end
mandel (generic function with 1 method)
julia> mandelperf() = [ mandel(complex(r,i)) for i=-1.:.1:1., r=-2.0:.1:0.5 ]
mandelperf (generic function with 1 method)
julia> @btime mandelperf()
72.942 μs (1 allocation: 4.44 KiB)
Целочисленное преобразование
Обратите внимание, что я удалил оператор объявления, поскольку он кажется ненужным для этого теста. Результаты моего теста показывают:
1. Raw — Numpy (2340 мкс) против Джулии (176 мкс).
2. Генерация случайных чисел по внешнему циклу — Cython (378 мкс) против Julia (176 мкс).
In [112]: def parse_int1_numpy():
...: for i in range(1,1000):
...: n = np.random.randint(0,2**31-1)
...: s = hex(n)
...: m = int(s,16)
In [113]: %timeit parse_int1_numpy()
2.34 ms ± 80 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Julia
julia> function parseintperf(t)
local n, m
for i=1:t
n = rand(UInt32)
s = hex(n)
m = UInt32(parse(Int64,s,16))
end
end
parseintperf (generic function with 1 method)
julia> @btime parseintperf(1000)
176.061 μs (2000 allocations: 93.75 KiB)
Цитон (внешний цикл)
In [116]: %%cython
...: import numpy as np
...: import cython
...:
...: @cython.boundscheck(False)
...: @cython.wraparound(False)
...: cpdef parse_int_vec_cython():
...: cdef:
...: long i,m
...: long[:] n
...: n = np.random.randint(0,2**31-1,1000)
...: for i in range(1,1000):
...: m = int(hex(n[i]),16)
...:
In [118]: %timeit parse_int_vec_cython()
378 µs ± 5.29 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Джулия (внешний цикл)
julia> function parseintperf2(t)
n = rand(UInt32, t)
for i=1:t
s = hex(n[i])
m = UInt32(parse(Int64,s,16))
end
end
parseintperf2 (generic function with 1 method)
julia> @btime parseintperf2(1000)
176.053 μs (2003 allocations: 97.97 KiB)
Ба, давайте вернемся к исходному сообщению в блоге.
язык
У автора есть некоторые критические замечания по поводу самого языка Julia:
Несбалансированный конец
Я лично не думаю, что это проблема, это личное предпочтение. Большинство языков имеют маркер конца блока. Но я не понимаю, что значит несбалансированный.
использовать::
Это не проблема, это личные предпочтения.
Неортодоксальный синтаксис оператора, пунктуации и многострочного комментария
Пунктуация Джулии не сильно отличается от многих языков программирования. Опять же, это похоже на вопрос личных предпочтений.
индексация на основе одной
это мой любимый. У обоих лагерей есть свои достоинства. Это действительно зависит от вашего использования и конкретной ситуации. Как уже отмечалось на форуме, есть некоторые пакеты (такие как OffserArray и PemutedDimsArray), которые используют 0 в качестве базы, что более естественно. Хорошо, по крайней мере, у нас есть варианты.
Стандартные системы документации используют Markdown
Я начал программировать с Джулией несколько месяцев назад и нашел систему документации интуитивно понятной и простой. Тем не менее, я думаю, что автор предоставляет больше информации в последующем блоге, и я думаю, что автор делает правильное замечание. Похоже, что мнения разделились.
В случае Джулии этот выбор был сделан на основе нескольких проблем в первые дни использования reST. После проведения всевозможных исследований. На мой взгляд, ReStructuredText более строгий и практичный, а Markdown проще в освоении и практическом применении.
В конце концов, здесь действительно нет правильного или неправильного. Мы знаем, что разработчики никогда не соглашаются ни в чем. К счастью, общее количество разработчиков может принимать собственные решения. Мы полностью уважаем проекты, которые выбирают ReStructuredText вместо Markdown и наоборот.
Безопасность
Автор считает, что Джулия склонна к ошибкам сегмента памяти при использовании ccall. Я обнаружил, что это не соответствует статус-кво версии Джулии. Я не могу сгенерировать ошибку сегмента памяти из-за неправильного имени библиотеки и типа данных. Однако я могу получить красивое сообщение об ошибке.
julia> val = ccall((:getenv, "libc.so.6"), Ptr{UInt8}, (Ptr{UInt8},), var)
ERROR: error compiling anonymous: could not load library "libc.so.6"
dlopen(libc.so.6.dylib, 1): image not found
julia> val = ccall((:getenv, "libc.dylib"), Ptr{UInt8}, (Ptr{UInt8},), 123)
ERROR: MethodError: no method matching unsafe_convert(::Type{Ptr{UInt8}}, ::Int64)
Closest candidates are:
unsafe_convert(::Type{Ptr{UInt8}}, ::Symbol) at pointer.jl:35
unsafe_convert(::Type{Ptr{UInt8}}, ::String) at pointer.jl:37
unsafe_convert(::Type{T<:Ptr}, ::T<:Ptr) where T<:Ptr at essentials.jl:152 ...
Printf/Sprintf
Автор, кажется, немного недоволен @sprintf и @printf, которые генерируют много макросов.
Возможно, тогда это было правдой, но сейчас есть много обходных путей. Это область, в которой происходят быстрые инновации. Некоторые пакеты Julia могут решить эту проблему. Например: Formatting.jl и StringLiterals.jl.
Следующая строка кода генерирует только 63 строки нативного кода по сравнению с более чем 500 строками, о которых автор говорил в прошлом.
julia> @code_native Formatting.printfmt("{1:>4s} {2:.2f}", "abc", 12)
Если я скомпилирую приведенный ниже код C, я получу только 21 строку инструкций по сборке. По-прежнему немного сложно превзойти язык C.
void f(char *buffer, const char *a, double b) {
sprintf(buffer, "this is a %s %g", a, b);
}
модульный тест
Автор считает, что библиотека модульного тестирования Джулии ограничена:
Библиотеки модульного тестирования довольно просты, по крайней мере, для C++ и Java. FactCheck, возможно, самый популярный выбор, но, если не считать странного API, он довольно ограничен и в значительной степени неразвит.
Когда я разрабатывал SASLib.jl, я использовал только Base.Test. Я думаю, что это очень удобно и делает именно то, что мне нужно. Автор упоминает пакет FactCheck, что кажется разумным дополнительным условием. Я раньше не пользовался FactCheck, поэтому не буду публиковать здесь дополнительную информацию.
развитие
Интересно, что автор сказал, что он рассматривал возможность участия в проекте Julia, но ему не понравилось, что среда выполнения Julia была построена из нескольких языков программирования C/C++, Lisp и Julia.
Я не понимаю, в чем дело. Если я посчитаю, сколько строк кода для каждого языка в Julia 0.6:
- Julia 175,147
- C/C++ 70,694
- Femtolisp 8,270
Код C/C++ очень нужен для базовой среды исполнения Julia. Затем большая часть языка Julia строится с использованием самого языка Julia. Итак, давайте обсудим Femtolisp 3,3% кода, который является схемоподобной реализацией lisp.
Недавно у меня был опыт участия в проекте Julia. Члены сообщества на форумах очень дружелюбны, а основная команда разработчиков всегда онлайн. Если я хочу внести больший вклад, нет никаких препятствий, и я могу легко получить большую помощь.
Наконец, автор приводит сторонний вопрос под названием «Как Юля затормозила и пропала из точки доступа». Я не знаю, какой была среда в 2015 году, но учтите, что Джулия только что получила начальное финансирование в 2017 году. Я ожидал, что он ускорится, а не замедлится. Время покажет, сохранится ли этот импульс, когда Джулия приблизится к отметке 1.0.
Наконец
Я надеюсь, что этот пост в блоге будет полезен всем, кто оценивает Джулию.
Рекомендуемые курсы
campus.swarma.org/gcou=388
campus.swarma.org/gcou=388(автоматическое распознавание QR-кода)
Учебное видео также опубликовано на Bilibili Danmu.com:
Видео адрес второй:вооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо по
Рекомендуемое чтение
Юлия язык минималистский вводный урок
Перевод: Ся Цзяхао
Оригинальный адрес:tk3369.wordpress.com