Как Юлия реализует мультиметоды?

Я читал немного из http://c2.com/cgi/wiki?ImplementingMultipleDispatch

У меня возникли проблемы с поиском ссылки на то, как Джулия реализует multimethods. Какова сложность выполнения диспетчеризации и как она этого достигает?

1 ответов


Диссертация доктора Безансона, безусловно, лучший источник для описания внутренних органов Джулии прямо сейчас:

4.3 диспетчерская система

диспетчерская система Julia сильно напоминает мультиметодные системы, найденные в некоторых объектно-ориентированных языках [17, 40, 110, 31, 32, 33]. Однако мы предпочитаем термин Type-based dispatch, поскольку наша система фактически работает, отправляя один тип кортежа аргументов. Разница тонкая и во многих случаи не заметны, но имеют важные концептуальные последствия. Это означает, что методы не обязательно ограничиваются указанием типа для каждого аргумента "слот". Например, сигнатура метода может быть Union{Tuple{Any,Int}, Tuple{Int,Any}}, который соответствует вызовам, где либо, но не обязательно оба, из двух аргументов является Int.2

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

самый большой ключ здесь заключается в том, что определения методов сортируются по спецификациям, поэтому это просто линейный поиск, чтобы проверить, является ли тип кортежа аргумента подтипом подписи. Так это просто O (n), верно? Беда в том, что проверка подтипы с полной общности (в том числе союзов и TypeVars и т. д.) сложно. Очень тяжело, на самом деле. Хуже, чем NP-complete, по оценкам, это ΠP2 (см. полиномиальной иерархии) - то есть, даже если П=НП, эта проблема будет еще не полиномиальное время! Это может быть даже PSPACE или хуже.


конечно, лучшим источником для того, как это на самом деле работает, является реализация в JuliaLang / julia / src / gf.c (gf = общая функция). Есть довольно полезный комментарий есть:

метод кэши делятся на три части: одна для подписей, где первый аргумент-одноэлементный вид (Type{Foo}), один индексироваться UID типа первого аргумента в обычных случаях и резервный таблица всего остального.

таким образом, ответ на ваш вопрос о сложности поиска метода:"это зависит." при первом вызове метода с новым набором типов аргументов он должен пройти этот линейный поиск, ища соответствие подтипа. Если он найдет его, он специализируется этот метод для конкретных аргументов и помещает его в один из этих кэшей. Это означает, что перед началом поиска жесткого подтипа Джулия может выполнить быструю проверку равенства против уже виденных методов... и количество методов, которые необходимо проверить, дополнительно уменьшается, поскольку кэши хранятся как хэш-таблицы на основе первого аргумента.

но, на самом деле, ваш вопрос был о время работы сложность отправки. В этом случае ответ довольно часто "что направить?" - потому что он был полностью исключены! Джулия использует LLVM как компилятор, который едва опережает время, в котором методы компилируются по требованию по мере необходимости. В коде performant Julia типы должны быть конкретно выведены во время компиляции, и поэтому отправка может и выполняется во время компиляции. Это полностью устраняет накладные расходы на отправку во время выполнения и потенциально даже упрощает найденный метод непосредственно в тело вызывающего абонента (если оно мало), чтобы удалить все накладные расходы на вызов функции и обеспечить дальнейшую оптимизацию вниз по течению. Если типы не выводятся конкретно, есть другие подводные камни производительности, и я не профилировал, сколько времени обычно тратится на отправку. Есть способы оптимизировать этот случай дальше, но, скорее всего, сначала нужно поджарить большую рыбу... и сейчас, как правило, проще всего сделать горячие петли стабильными в первую очередь.