yield from

The current effort is to improve the speed of python-on-guile. To see a nice result consider the following python one liner,

print(sum(1 for x in range(1000_000_000)

The result with python-on-guile is, 1.6s and for python3 we have 27.9s on my machine.

But the interesting stuff is when we use the optimized one shot continuations to improve generator speeds in guile which must be implemented with delimeted continuations if plain guile is to be used, the current state are that for the code,

x = (x for x in range(250_000_000))
print(sum(x))

We basically loop 2 million times with vanilla python on guile and for the modified guile version with one shot continuations, we have a speed of about 60 million iterations per second. Interestingly python3 boost a 35 million iterations per second.

So let's up this even more lets consider this code,

def f(n):
    yield from n

x = (x for x in range(100_000_000))

print(sum(f(f(f(f(f(f(f(f(f(f(x))))))))))))

Whatever the number applications of \(f\) we do, teh modified python on guile stays on about 2s to perform the iterations. Cpython and vanilla python-on-guile will grow in complexity as we nest more.

So what's the trick. The trick is that in the "yield from" loop it will start asking for the generator's stack and will also transfer all values straight through. And a normal generator loop, when it get's a stack frame, will push the old stack onto a stack of stacks and continue using the new one, if it get's an end of iteration signal it will then pop a stack on the stack of stacks if non empty and continue iterating with that one. This means that the actual looping will be communicating with the innermost stack and hence there will not be any overhead appart for the generator stuff if the loop has a reasonable high number of counts. Of cause when the stack of stack is empty and we get an end of iteration signal we have done all the iterations.

links

social