Использование параметра типа NTuple c против использования абстрактного вектора в Julia - PullRequest
4 голосов
/ 28 апреля 2020

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

struct Binary{T <: BinaryKind} <: Operation
    xs::Vector{Union{Operation, Atom}}
end

, где Operation и Atom являются абстрактными типами. Прочитав советы по повышению производительности в документации по julia, я понял, что более эффективным способом представления этой структуры будет

struct Binary{T <: BinaryKind, D <: Tuple} <: Operation
    xs::D
end

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

1 Ответ

1 голос
/ 30 апреля 2020

Я думаю (слишком долго для комментария, поэтому я даю его в качестве ответа), что в этом случае, вероятно, лучше быть нестабильным. Обратите внимание, что именно это и делает сама Джулия:

julia> x = :(1+2*(3+4))
:(1 + 2 * (3 + 4))

julia> dump(x)
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol +
    2: Int64 1
    3: Expr
      head: Symbol call
      args: Array{Any}((3,))
        1: Symbol *
        2: Int64 2
        3: Expr
          head: Symbol call
          args: Array{Any}((3,))
            1: Symbol +
            2: Int64 3
            3: Int64 4

Конечно, у Джулии значительно более богатый синтаксис, но даже в вашем простом случае учтите следующее. Вы получаете преимущества стабильности типов, если один раз скомпилируете некоторую часть кода, а затем многократно запускаете ее (в той или иной форме).

Теперь я предполагаю, что написанное вами будет оцениваться на практике в основном только одним время. Если вы сделали ваши выражения полностью стабильными, вы должны будете платить каждый раз (при условии, что выражение меняется):

  1. стоимость его компиляции (дорого)
  2. стоимость его запуска (относительно дешево)

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

С другой стороны - если вы ожидаете, что вы определите выражение только один раз, а затем запустите его во многих случаях, возможно, лучше использовать метапрограммирование:

  1. обрабатывает ваше выражение только один раз и генерирует код Джулии, который будет оценивать ваше выражение
  2. затем Джулия скомпилирует сгенерированный код, как только
  3. вы получите максимальную производительность при его выполнении после выполнения шагов 1 и 2

Полумера к вашему вопросу будет использовать следующую структуру данных:

struct Binary{T <: BinaryKind, S} <: Operation
    xs::Vector{S}
end

Таким образом, ваш код будет устойчивым к типам, если S является конкретным типом или небольшим объединением конкретных типов, и нестабильным типом в противном случае (и я ожидаю, что тогда вы можете попытаться заставить остальную часть кода сгенерировать xs таким образом, чтобы eltype был конкретным или небольшим объединением конкретных типов).

(если вы Есть еще вопросы по этому вопросу, пожалуйста, прокомментируйте, и я могу расширить ответ)

...