Передача указателей переменных в Julia

Можно ли использовать в Юлии что-то вроде указателей или ссылок, как в C / C ++ или C #? Мне интересно, потому что будет полезно передавать тяжелые объекты как указатель / ссылку, но не как значение. Например, использование памяти указателей для хранения объекта может быть выделено один раз для всей программы, а затем указатель может быть передан через программу. Как я могу себе представить, это повысит производительность памяти и использование вычислительной мощности.

Простой код на C ++, показывающий, что я пытаюсь выполнить в Джулии:

#include <iostream> 

void some_function(int* variable){       // declare function 
    *variable += 1;                      // add a value to the variable
}

int main(){                                
    int very_big_object = 1;             // define variable
    some_function( &very_big_object );   // pass pointer of very_big_object to some_function
    std::cout << very_big_object;        // write value of very_big_object to stdout

    return 0;                            // end of the program
}

Выход:

2

Создается новый объект, его указатель затем передается в some_funciton, который изменяет этот объект, используя переданный указатель. Возврат нового значения не требуется, поскольку программа редактировала исходный объект, а не копию. После выполнения some_function значение переменной печатается, чтобы увидеть, как оно изменилось.

См. также:  Указатели на шаблонный класс
Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 2
  1. dawidpieciul

    Хотя вы можете вручную получить указатели на объекты Julia, на самом деле это не обязательно для получения желаемой производительности и результатов. Как указано в руководстве :

    Аргументы функции Julia следуют соглашению, которое иногда называют передачей по совместному использованию, что означает, что значения не копируются при передаче в функции. Сами аргументы функции действуют как новые привязки переменных (новые местоположения, которые могут ссылаться на значения), но значения, на которые они ссылаются, идентичны переданным значениям. Изменения изменяемых значений (например, массивов), сделанные в функции, будут видны вызывающей стороне. Это то же поведение, что и в Scheme, большинстве Lisps, Python, Ruby и Perl, среди других динамических языков.

    следовательно, вы можете просто передать объект функции в обычном режиме, а затем работать с ним на месте. Функции, которые изменяют свои входные аргументы (включая любые функции на месте) по соглашению в Julia, имеют имена, оканчивающиеся на !. Единственная загвоздка в том, что вы должны быть уверены, что в теле функции не сделаете ничего, что могло бы вызвать копирование объекта.

    Так, например

    function foo(A)
        B = A .+ 1 # This makes a copy, uh oh
        return B
    end
    function foo!(A)
        A .= A .+ 1 # Mutate A in-place
        return A # Technically don't have to return anything at all, but there is also no performance cost to returning the mutated A
    end
    

    (обратите внимание, в частности, на . перед .= во второй версии; это очень важно. Если бы мы не использовали это, мы фактически не мутировали бы A, а просто переназначили бы имя A (внутри область действия функции) для ссылки на результат RHS, так что это фактически было бы полностью эквивалентно первой версии, если бы не это .)

    Если мы затем протестируем их, вы четко увидите разницу:

    julia> large_array = rand(10000,1000);
    
    julia> using BenchmarkTools
    
    julia> @benchmark foo(large_array)
    BechmarkTools.Trial: 144 samples with 1 evaluations.
     Range (min … max):  23.315 ms … 78.293 ms  ┊ GC (min … max):  0.00% … 43.25%
     Time  (median):     27.278 ms              ┊ GC (median):     0.00%
     Time  (mean ± σ):   34.705 ms ± 12.316 ms  ┊ GC (mean ± σ):  23.72% ± 23.04%
    
       ▁▄▃▄█ ▁
      ▇█████▇█▇▆▅▃▁▃▁▃▁▁▁▄▁▁▁▁▃▁▁▃▁▁▁▁▁▁▁▁▁▁▁▁▁▅▃▄▁▄▄▃▄▆▅▆▃▅▆▆▆▅▃ ▃
      23.3 ms         Histogram: frequency by time        55.3 ms <
    
     Memory estimate: 76.29 MiB, allocs estimate: 2.
    
    julia> @benchmark foo!(large_array)
    BechmarkTools.Trial: 729 samples with 1 evaluations.
     Range (min … max):  5.209 ms …   9.655 ms  ┊ GC (min … max): 0.00% … 0.00%
     Time  (median):     6.529 ms               ┊ GC (median):    0.00%
     Time  (mean ± σ):   6.845 ms ± 955.282 μs  ┊ GC (mean ± σ):  0.00% ± 0.00%
    
                ▁ ▄▇██▇▆ ▁ ▁
      ▂▂▃▂▄▃▇▄▅▅████████▇█▆██▄▅▅▆▆▄▅▆▇▅▆▄▃▃▄▃▄▃▃▃▂▂▃▃▃▃▃▃▂▃▃▃▂▂▂▃ ▃
      5.21 ms         Histogram: frequency by time        9.33 ms <
    
     Memory estimate: 0 bytes, allocs estimate: 0.
    

    Обратите особое внимание на разницу в использовании и распределении памяти в дополнение к разнице во времени ~ 4x. На данный момент практически все время тратится на фактическое добавление, поэтому единственное, что нужно оптимизировать, если это критичный для производительности код, — это убедиться, что код эффективно использует все ваши CPU векторные регистры и инструкции SIMD, которые можно сделать с помощью LoopVectorization.jl:

    using LoopVectorization
    function foo_simd!(A)
       @turbo A .= A .+ 1 # Mutate A in-place. Equivalentely @turbo @. A = A + 1
       return A 
    end
    
    julia> @benchmark foo_simd!(large_array)
    BechmarkTools.Trial: 986 samples with 1 evaluations.
     Range (min … max):  4.873 ms …   7.387 ms  ┊ GC (min … max): 0.00% … 0.00%
     Time  (median):     4.922 ms               ┊ GC (median):    0.00%
     Time  (mean ± σ):   5.061 ms ± 330.307 μs  ┊ GC (mean ± σ):  0.00% ± 0.00%
    
      █▇▄▁▁   ▂▂▅▁
      █████████████▆▆▇▆▅▅▄▅▅▅▆▅▅▅▅▅▅▆▅▅▄▁▄▅▄▅▄▁▄▁▄▁▅▄▅▁▁▄▅▁▄▁▁▄▁▄ █
      4.87 ms      Histogram: log(frequency) by time       6.7 ms <
    
     Memory estimate: 0 bytes, allocs estimate: 0.
    

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

    Теперь, если по какой-либо причине вам все еще нужен буквальный указатель, вы всегда можете получить его с помощью Base.pointer, хотя обратите внимание, что это сопровождается некоторыми существенными оговорками и, как правило, не то, что вам нужно.

    help?> Base.pointer
      pointer(array [, index])
    
      Get the native address of an array or string, optionally at a given location index.
    
      This function is "unsafe". Be careful to ensure that a Julia reference to array exists
      as long as this pointer will be used. The [email protected] macro should be used to protect
      the array argument from garbage collection within a given block of code.
    
      Calling Ref(array[, index]) is generally preferable to this function as it guarantees
      validity.
    
  2. dawidpieciul

    Хотя Джулия использует передачу по совместному использованию, и обычно вам не нужно / не хотите использовать указатели, в некоторых случаях вы действительно хотите это сделать, и вы можете!

    Вы создаете указатели с помощью Ref{Type}, а затем уважаете их с помощью []. Рассмотрим следующую функцию, изменяющую свой аргумент.

    function mutate(v::Ref{Int})
           v[] = 999
    end
    

    Это можно использовать следующим образом:

    julia> vv = Ref(33)
    Base.RefValue{Int64}(33)
    
    julia> mutate(vv);
    
    julia> vv
    Base.RefValue{Int64}(999)
    
    julia> vv[]
    999
    

    Для более подробного обсуждения передачи ссылки в Julia, пожалуйста, посмотрите этот пост Как передать объект по ссылке и значению в Julia?

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: