Есть ли разница между забавой (n :: Integer) и забавой (n :: T), где T <: целое число в производительности / генерации кода? - PullRequest
0 голосов
/ 02 мая 2018

В Джулии я чаще всего вижу код, написанный как fun(n::T) where T<:Integer, когда функция работает для всех подтипов Integer. Но иногда я также вижу fun(n::Integer), что, как утверждают некоторые руководства, эквивалентно приведенному выше, в то время как другие говорят, что это менее эффективно, потому что Джулия не специализируется на конкретном подтипе, если только на подтип T явно не ссылаются.

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

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Оба определения эквивалентны. Обычно вы будете использовать форму fun(n::Integer) и применять fun(n::T) where T<:Integer только в том случае, если вам нужно использовать конкретный тип T непосредственно в коде. Например, рассмотрим следующие определения из базы (все следующие определения также из базы), где он имеет естественное использование:

zero(::Type{T}) where {T<:Number} = convert(T,0)

или

(+)(x::T, y::T) where {T<:BitInteger} = add_int(x, y)

И даже если вам во многих случаях требуется информация о типе, достаточно использовать функцию typeof. Опять пример определения:

oftype(x, y) = convert(typeof(x), y)

Даже если вы используете параметрический тип, вы часто можете избегать использования предложения where (немного подробного), например:

median(r::AbstractRange{<:Real}) = mean(r)

потому что вас не волнует фактическое значение параметра в теле функции.

Теперь - если вы пользователь Юлии, как я, - вопрос в том, как убедить себя, что это работает, как и ожидалось. Существуют следующие методы:

  • вы можете проверить, что одно определение перезаписывает другое в таблице методов (т. Е. После оценки обоих определений для этой функции присутствует только один метод);
  • вы можете проверить код, сгенерированный обеими функциями, используя @code_typed, @code_warntype, @code_llvm или @code_native и т. Д. И выяснить, что это один и тот же
  • наконец, вы можете сравнить код производительности с помощью BenchmarkTools

Хороший сюжет, объясняющий, что Джулия делает с вашим кодом, находится здесь http://slides.com/valentinchuravy/julia-parallelism#/1/1 (я также рекомендую всю презентацию любому пользователю Джулии - это отлично). И вы можете видеть на нем, что Джулия после понижения AST применяет шаг вывода типа, чтобы специализировать вызов функции перед шагом кодирования LLVM.

Вы можете намекнуть компилятору Julia, чтобы избежать специализации. Это делается с помощью макроса @nospecialize на Julia 0.7 (хотя это только подсказка).

0 голосов
/ 02 мая 2018

Да Bogumił Kamiński правильно в своем комментарии: f(n::T) where T<:Integer и f(n::Integer) будут вести себя точно так же, за исключением того, что прежний метод будет иметь имя T, уже определенное в его теле , Конечно, в последнем случае вы можете просто явно присвоить T = typeof(n), и он будет вычислен во время компиляции.

Есть несколько других случаев, когда использование TypeVar, как это, крайне важно, хотя, и, вероятно, стоит их вызвать:

  • f(::Array{T}) where T<:Integer действительно очень отличается от f(::Array{Integer}). Это обычная параметрическая инвариантность гоча ( документы и еще один вопрос SO об этом).
  • f(::Type) будет генерировать только одну специализацию для всех DataType с. Поскольку типы очень важны для Джулии, сам тип Type является особенным и позволяет параметризацию, например Type{Integer}, чтобы вы могли указать просто тип Integer. Вы можете использовать f(::Type{T}) where T<:Integer, чтобы Джулия специализировалась на точном типе Type, который он получает в качестве аргумента, разрешая Integer или любые его подтипы.
...