试图找到一种方法来构建朱莉娅`generator'

时间:2015-03-30 05:22:53

标签: generator julia

我是朱莉娅的新手。 我主要用python编程。

在python中, 如果你想迭代一大堆值, 通常构造一个所谓的生成器来节省内存使用量。 这是一个示例代码:

def generator(N):
    for i in range(N):
        yield i

我想知道朱莉娅是否有类似内容。 看完朱莉娅手册后, @task宏似乎与python中的生成器具有相同(或类似)的功能。 然而, 经过一些实验, 在julia中,内存使用量似乎比通常的数组大。

我在IJulia中使用@time来查看内存使用情况。 这是我的示例代码:

[更新]:添加generator方法的代码
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 个答案:

答案 0 :(得分:12)

我推荐Carl Vogel的"tricked out iterators"博客文章,其中详细讨论了julia的迭代器协议,任务和协同例程。

另请参阅julia文档中的task-aka-coroutines


在这种情况下,您应该使用Range类型(定义迭代器协议):

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

分配的内存更快,内存更少(就像在python 2中的xrange一样)。

博文中的摘录:

  

https://github.com/JuliaLang/julia/blob/master/base/range.jl开始,这里是如何定义Range的迭代器协议:

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中迭代器的值。这与Array迭代器不同,后者只从内存中读取元素a [i]。

     

利用此类延迟评估的迭代器可以带来重要的性能优势。如果我们想迭代整数1到10,000,迭代一个数组意味着我们必须分配大约80MB来保存它。范围只需要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

注意:如果用此替换Range,性能会更差(并分配更多内存):

julia> @time fun_arry()
elapsed time: 0.699122659 seconds (9 MB allocated)
5000050000

答案 1 :(得分:7)

很久以前,这个问题被提出(并得到了回答)。由于这个问题在谷歌搜索中排名很高,我想提一下问题和答案都已过时。

如今,我建议使用一个实现类似Python的yield生成器的宏来查看https://github.com/BenLauwens/ResumableFunctions.jl Julia库。

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。 (频道已经取代了问题中提到的旧的produce()函数和之前的答案)。

话虽如此,如果性能不是问题,我仍然建议推入通道作为第一种方法,因为在编译功能时可恢复功能有时可能很挑剔并且偶尔会遇到一些最坏情况的行为。特别是,因为它是一个编译为FSM而不是函数的宏,所以你当前需要注释Resumable函数中所有变量的类型以获得良好的性能,这与vanilla Julia函数不同,其中当函数是JIT时由JIT处理第一次打电话。

答案 2 :(得分:4)

我认为TextChanged已被Task取代。 Ben Lauwens的Fibonacci生成器的用法是:

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