Hi. First thread.
we all know the generator pattern from Python and elsewhere.
def mygenerator(): for i in range(10): yield i
I know the “proper” way to do this is to write a struct with a method on
Base.iterate, but sometimes you don’t need to / want to implement a struct for something that is more easily described in terms of procedures. I’ve seen the pattern with Channels floating around online, and I know using closures is also a popular strategy for this in other languages. i.e.
function countto(n) count = 0 () -> (count >= n ? nothing : count += 1) end function closuretest() a_cool_million = countto(1_000_000) while (i = a_cool_million()) != nothing end end function countto2(n) Channel(ctype=Int) do c count = 0 while count >= n put!(c += 1, count) end end end function channeltest() for i in countto2(1_000_000) end end
I like the way the channel version a little more because I like that it automatically provides an interface for for loops and, to my eye, the code is a little easier to with the loop than with the closure.
then I ran the code a few times to see how each would look in terms of performance:
@time closuretest() @time closuretest() @time closuretest() @time closuretest() println() @time channeltest() @time channeltest() @time channeltest() @time channeltest()
(did the extra runs to make sure the JIT was warm)
0.064392 seconds (2.01 M allocations: 30.837 MiB) 0.051788 seconds (2.00 M allocations: 30.510 MiB, 3.74% gc time) 0.049927 seconds (2.00 M allocations: 30.510 MiB, 7.13% gc time) 0.046646 seconds (2.00 M allocations: 30.510 MiB, 0.60% gc time) 0.189093 seconds (331.03 k allocations: 16.900 MiB, 1.23% gc time) 0.000072 seconds (31 allocations: 2.266 KiB) 0.000045 seconds (31 allocations: 2.266 KiB) 0.000045 seconds (31 allocations: 2.266 KiB)
So, yeah, wasn’t expecting that. Obviously the channel generator pattern is better from a performance perspective, which I wouldn’t have expected, since it has to hit the scheduler on every iteration (or not?)
Anyway, I don’t put too much stock in the current poor performance of closures, since that can be fixed. I’m mostly just wondering which pattern is considered more idiomatic in Julia.