Оптимизация кода Юлии, разница между структурами и примитивными типами?(память выделяет) - PullRequest
0 голосов
/ 31 мая 2018

У меня есть код для оптимизации с некоторыми критическими частями, для которых я не хочу, чтобы gc запускал выделение памяти.

Точнее, у меня есть тип действительного числа

struct AFloat{T<:AbstractFloat} <: Real
    value::T
    j::Int 
end

Я должен отслеживать, чтобы выполнить автоматическое дифференцирование.Таким образом, для любой арифметической операции мне нужно сделать несколько записей в ленте.Производительность здесь очень важна (она имеет реальное значение, если у вас есть еще один ассигнт на арифметическую операцию!)У меня есть выбор между AFloat{T} или просто использовать примитивный тип для отслеживания индекса j:

primitive type AFloat64 <: Real sizeof(Int) end

Однако меня смущают эти результаты:

Первая часть: ok

using BenchmarkTools

struct A n::Int64 end

vA=A[A(1)];
@time push!(vA,A(2))

v=Int64[1];
@time push!(v,2)

возвращает

0.000011 seconds (6 allocations: 224 bytes)
0.000006 seconds (5 allocations: 208 bytes)

, что соответствует:

@btime push!(vA,A(2))
@btime push!(v,2)

, который возвращает

  46.410 ns (1 allocation: 16 bytes)
  37.890 ns (0 allocations: 0 bytes)

-> Я бы пришел к выводу, что нажатие примитивного типа позволяет избежать одного выделения памяти по сравнению с struct (верно?)

Часть вторая: ... проблематично ...?!

Здесь я запутался и не могу интерпретировать эти результаты:

foo_A() = A(1);             
foo_F64() = Float64(1.);                
foo_I64() = Int64(1);             

@time foo_A()
@time foo_F64()
@time foo_I64()

возвращает

  0.000004 seconds (5 allocations: 176 bytes)
  0.000005 seconds (5 allocations: 176 bytes)
  0.000005 seconds (4 allocations: 160 bytes)

Q1, как интерпретировать разницу foo_F64 () против foo_I64 () (5 распределений против 4 распределений)?

Более того, результаты кажутся несовместимыми с выходами @btime:

@btime foo_A()
  3.179 ns (0 allocations: 0 bytes)

@btime foo_F64()
  3.801 ns (0 allocations: 0 bytes)

@btime foo_I64()
  3.180 ns (0 allocations: 0 bytes)

Q2: какой правильный ответ @time или @btime?Почему?

Если быть синтетическим , в Джулии, есть ли разница в распределении памяти и памяти между foo_A и foo_Primitive , где:

struct A n::Int64 end

foo_A() = A(1)
foo_Primitive() = Int64(1)

Мне известно, что при использовании таких небольших выражений существует реальный риск побочных эффектов при использовании @time или @btime.В идеале было бы лучше иметь некоторые знания о внутренностях Джулии, чтобы ответить.Но я не


julia> versioninfo()
Julia Version 0.6.2
Commit d386e40c17 (2017-12-13 18:08 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Xeon(R) CPU E5-2603 v3 @ 1.60GHz
  WORD_SIZE: 64
  BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell)
  LAPACK: libopenblas64_
  LIBM: libopenlibm
  LLVM: libLLVM-3.9.1 (ORCJIT, haswell)

1 Ответ

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

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

julia> function f()
           vA=[A(1)]
           @time push!(vA,A(2))

           v=[1]
           @time push!(v,2)
       end
f (generic function with 1 method)

julia> f();
  0.000000 seconds (1 allocation: 32 bytes)
  0.000000 seconds (1 allocation: 32 bytes)
...