Почему поощряется детекторизация в Юлии?

Кажется, что в Юлии поощряется писать детекционированный код. Существует даже пакет, который пытается сделать это за вас.

Мой вопрос: почему?

Прежде всего, исходя из аспекта пользовательского опыта, векторизованный код более краток (меньше кода, затем меньше вероятности ошибок), более понятный (отсюда легче отлаживать), более естественный способ написания кода (по крайней мере для кого-то который исходит из научного компьютерного фона, к которому Джулия пытается удовлетворить). Возможность написать что-то вроде vector'vector или vector'Matrix*vector очень важна, потому что это соответствует фактическому математическому представлению, и именно так думают научные мыслители об этом в голове (а не в вложенных циклах). И я ненавижу тот факт, что это не лучший способ написать это, и переустановка его в циклы будет быстрее.

В настоящий момент кажется, что существует конфликт между целью написания кода, который выполняется быстро, и сжатым/понятным кодом.

Во-вторых, какова техническая причина этого? Хорошо, я понимаю, что векторизованный код создает дополнительные временные и т.д., Но векторизованные функции (например, broadcast(), map() и т.д.) Имеют потенциал многопоточности, и я думаю, что преимущество многопоточности может перевесить накладные расходы временных и других недостатков векторизованных функций, что делает их более быстрыми, чем обычные для циклов.

Имеют ли текущие реализации векторизованных функций в Юлии скрытые многопоточности под капотом?

Если нет, есть ли работа/планирует добавить неявные concurrency в векторизованные функции и сделать их быстрее, чем циклы?

Ответ 1

Для легкого чтения я решил превратить свой комментарий в марафон выше в ответ.

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

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

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

К моменту появления v1.0 наиболее векторный код должен быть таким же быстрым или быстрым, как эквивалентный код в Matlab. Во многих случаях эта цель развития уже достигнута, поскольку многие векторные/матричные операции в Julia отправляются в соответствующие процедуры BLAS компилятором.

Что касается многопоточности, в настоящее время для Джулии реализуется собственная многопоточность, и я полагаю, что экспериментальный набор подпрограмм уже доступен на основной ветке. Соответствующая страница проблемы здесь. Неявная многопоточность для некоторых векторных/матричных операций уже теоретически доступна в Юлии, поскольку Джулия называет BLAS. Я не уверен, что он включен по умолчанию.

Помните, что многие векторизованные операции будут (в настоящее время) намного быстрее в MATLAB, поскольку MATLAB уже много лет пишет специализированные многопоточные библиотеки C, а затем вызывает их под капотом. После того, как у Джулии есть многопоточность, я думаю, что Julia обойдет MATLAB, так как в этот момент сообщество разработчиков Dev может расчистить стандартные пакеты Julia и обновить их, чтобы по возможности использовать преимущества многопоточности.

Напротив, MATLAB не имеет встроенной многопоточности, поэтому вы полагаетесь на Mathworks для предоставления специализированных многопоточных подпрограмм в виде базовых библиотек C.

Ответ 2

Вы можете и писать vector'*matrix*vector (или, возможно, dot(vector, matrix*vector), если вы предпочитаете скалярный вывод). Для таких вещей, как матричное умножение, вам гораздо лучше использовать векторизованную нотацию, потому что она вызывает базовые библиотеки BLAS, которые более сильно оптимизированы, чем код, созданный в основном любой комбинацией языка/компилятора.

В других местах, как вы говорите, вы можете извлечь выгоду из детекторизации, избегая временных промежуточных продуктов: например, если x является вектором, выражение

y = exp(x).*x + 5

создает 3 временных вектора: один для a = exp(x), один для b = a.*x и один для y = b + 5. Напротив,

y = [exp(z)*z+5 for z in x]

не создает временных промежуточных элементов. Поскольку циклы и понимание в julia бывают быстрыми, нет недостатка в написании детекционированной версии, и на самом деле она должна работать немного лучше (особенно с комментариями производительности, например @simd, где это необходимо).

Приход потоков может изменить ситуацию (делая векторизованный exp быстрее, чем "наивный" exp), но в целом я бы сказал, что вы должны рассматривать это как "ортогональную" проблему: julia, скорее всего, сделает многопоточность настолько простой в использовании, что вы сами можете писать операции с использованием нескольких потоков, и, следовательно, векторная "библиотека" не имеет никакого преимущества по сравнению с кодом, который вы могли бы написать сами. Другими словами, вы можете использовать несколько потоков, но при этом писать детекционированный код, чтобы избежать этих временных рядов.

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

Ваше утверждение о том, что "векторизованный код всегда более краткий и более понятный", однако, не соответствует действительности: много раз при написании кода Matlab вам нужно идти в крайности, чтобы придумать векторизованный способ написания того, что на самом деле простые операции, когда думают в терминах циклов. Вы можете искать списки рассылки для бесчисленных примеров; тот, который я помню на SO, Как найти связанные компоненты в матрице с помощью Julia.