Отказаться от Юлии? Что не так с длинным текстовым разбором сомнений Юлии

искусственный интеллект Python Julia NumPy Cython

предисловие

Совсем недавно была официально запущена версия 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