Пытаюсь найти способ сконструировать "генератор" Джулии.
Я новичок в Юля. Я в основном программирую на python.
в python, если вам нужно перебрать множество значений, типично построить так называемый генератор для сохранения использования памяти. Вот один пример кода:
def generator(N):
for i in range(N):
yield i
я использую @time
в IJulia, чтобы увидеть использование памяти.
Вот мой пример кода:
[Update]: добавьте код для generator
метод
(The generator
способ)
function generator(N::Int)
for i in 1:N
produce(i)
end
end
(генератор версия)
function fun_gener()
sum = 0
g = @task generator(100000)
for i in g
sum += i
end
sum
end
@time fun_gener()
прошедшее время: 0.420731828 секунд (выделено 6507600 байт)
(массив версия)
function fun_arry()
sum = 0
c = [1:100000]
for i in c
sum += i
end
sum
end
@time fun_arry()
прошедшее время: 0.000629629 секунд (Выделено 800144 байта)
может кто-нибудь сказать мне, почему @task
в этом случае потребуется больше места?
И если я хочу сохранить использование памяти как дело с большим набором значений,
что я могу сделать?
3 ответов
рекомендую "навороченного итераторы" blogpost Карла Фогеля, который обсуждает протокол итератора Джулии, задачи и совместные процедуры в некоторых деталях.
см. также task-aka-coroutines в документах julia.
в этом случае вы должны использовать тип диапазон (который определяет итератор протокол):
julia> function fun_arry()
sum = 0
c = 1:100000 # remove the brackets, makes this a Range
for i in c
sum += i
end
sum
end
fun_arry (generic function with 1 method)
julia> fun_arry() # warm up
5000050000
julia> @time fun_arry()
elapsed time: 8.965e-6 seconds (192 bytes allocated)
5000050000
быстрее и меньше выделенной памяти (так же, как xrange
в python 2).
фрагмент из блога:
от https://github.com/JuliaLang/julia/blob/master/base/range.jl, Вот как определяется протокол итератора диапазона:
start(r::Ranges) = 0 next{T}(r::Range{T}, i) = (oftype(T, r.start + i*step(r)), i+1) next{T}(r::Range1{T}, i) = (oftype(T, r.start + i), i+1) done(r::Ranges, i) = (length(r) <= i)
обратите внимание, что следующий метод вычисляет значение итератора в состоянии i. Это отличается от итератора массива, который просто считывает элемент a[i] из памяти.
итераторы, которые используют отложенную оценку, как это может имейте важные преимущества представления. Если мы хотим перебирать целые числа от 1 до 10 000, перебор массива означает, что мы должны выделить около 80 МБ для его хранения. Диапазон требует только 16 байт; тот же размер, что и диапазон от 1 до 100 000 или от 1 до 100 000 000.
вы можете написать метод генератора (использование задач):
julia> function generator(n)
for i in 1:n # Note: we're using a Range here!
produce(i)
end
end
generator (generic function with 2 methods)
julia> for x in Task(() -> generator(3))
println(x)
end
1
2
3
Примечание: Если вы замените диапазон этим, производительность будет намного хуже (и выделит больше памяти):
julia> @time fun_arry()
elapsed time: 0.699122659 seconds (9 MB allocated)
5000050000
этот вопрос был задан (и ответил) довольно давно. Поскольку этот вопрос занимает высокое место в поиске google, я хотел бы упомянуть, что и вопрос, и ответ устарели.
В настоящее время, я бы предложил проверить https://github.com/BenLauwens/ResumableFunctions.jl для библиотеки Julia с макросом, который реализует генераторы доходности, подобные Python.
using ResumableFunctions
@resumable function fibonnaci(n::Int) :: Int
a = 0
b = 1
for i in 1:n-1
@yield a
a, b = b, a+b
end
a
end
for fib in fibonnaci(10)
println(fib)
end
поскольку сфера его применения гораздо более ограничены, чем полный сопрограммы, это тоже заказ величины более эффективной, чем нажатие значений в канал, так как он может скомпилировать генератор в FSM. (Каналы заменили старую функцию Product (), упомянутую в вопросе и предыдущих ответах).
С учетом сказанного, я бы все равно предложил нажать на канал в качестве первого подхода, если производительность не является проблемой, потому что resumablefunctions иногда могут быть привередливыми при компиляции вашей функции и иногда могут поражать какое-то худшее поведение. В частности, потому что это макрос, который компилируется в FSM, а не в функцию, в настоящее время вам нужно аннотировать типы всех переменных в Resumablefunction, чтобы получить хорошую производительность, в отличие от функций vanilla Julia, где это обрабатывается JIT при первом вызове функции.
думаю, что Task
был заменен Channel()
. Использование в терминах генератора Фибоначчи Бена Лаувенса:
fibonacci(n) = Channel(ctype=Int) do c
a = 1
b = 1
for i in 1:n
push!(c, a)
a, b = b, a + b
end
end
его можно использовать используя
for a in fibonacci(10)
println(a)
end
1
1
2
3
5
8
13
21
34
55