2010/12/8 Vitja Makarov <vitja.maka...@gmail.com>:
> 2010/12/7 Vitja Makarov <vitja.maka...@gmail.com>:
>> 2010/12/7 Vitja Makarov <vitja.maka...@gmail.com>:
>>> 2010/12/7 Robert Bradshaw <rober...@math.washington.edu>:
>>>> First off, great to hear you're working on this. I've wanted to do it
>>>> for a while (well, essentially ever since I got closures done), but
>>>> don't know when I'll find the time.
>>>>
>>>>
>>>> On Mon, Dec 6, 2010 at 12:56 AM, Vitja Makarov <vitja.maka...@gmail.com> 
>>>> wrote:
>>>>
>>>>>>> def my_generator_body(closure, value, is_exception):
>>>>>>
>>>>>> Shouldn't that be a cdef function?
>>>>>
>>>>> Guess no. It has very special signature and should be used only in
>>>>> generator closure.
>>>>>
>>>>> It can't be cdef as cdef doesn't parse (args, kwargs) and generator
>>>>> does this on first run.
>>>>
>>>> Eventually the argument parsing code should be abstracted out so that
>>>> we don't have this restriction.
>>>>
>>>>>>>       # switch case
>>>>>>>       switch (closure.resume_lable):
>>>>>>>           case 0:
>>>>>>>                  goto parse_arguments;
>>>>>>>           case 1:
>>>>>>>                  goto resume_from_yield1;
>>>>>>>       parse_arguments:
>>>>>>>       # function body here
>>>>>>
>>>>>> Ok, sure, as in the CEP.
>>>>>>
>>>>>>
>>>>>>> About temps:
>>>>>>> now temps are allocated in closure using declare_var() and it works 
>>>>>>> fine.
>>>>>>
>>>>>> I think that could seriously slow down things inside of the generator,
>>>>>> though. It means that it needs to jump through the closure indirection 
>>>>>> for
>>>>>> every little value that it keeps around temporarily, including lots of
>>>>>> intermediate C results. Also, I would expect that a lot more temps are 
>>>>>> used
>>>>>> inside of the generator body than what actually needs to be kept alive
>>>>>> during yields, so a single yield inside of a lengthy generator body could
>>>>>> have a tremendous impact on the overall runtime and closure size.
>>>>>>
>>>>>> If you only store away the live temp values before a yield, you don't 
>>>>>> even
>>>>>> need to do any reference counting for them. It's just a quick bunch of C
>>>>>> assignments on yield and resume. That's about the smallest impact you 
>>>>>> can get.
>>>>>>
>>>>>> BTW, if you change the object struct of the generator closure anyway, 
>>>>>> maybe
>>>>>> a dynamically sized PyVarObject would be a way to incorporate the temps?
>>>>>>
>>>>>>
>>>>>
>>>>> I don't understand why do you think about performance problems?
>>>>>
>>>>> It seems to me that if temps are stored in tuple (or another kind of
>>>>> object) you have to manully save/restore them, and this code will
>>>>> really slow down generator.
>>>>
>>>> -1 to storing them all in a tuple. That wouldn't handle C values very
>>>> well, for one thing. I wouldn't use arrays though--either declare each
>>>> temp individually or create a custom struct that has the correct slot
>>>> for each temp. Copy to locals on enter, copy out on
>>>> yield/exception/return.
>>>>
>>>>> On the other hand when all temps are declared in closure you just have
>>>>> to say "closure->pyx_temp_v1"
>>>>>
>>>>> Do you think that closure-> will be much slower then stack operations?
>>>>
>>>> Yes, the performance difference can be dramatic. This is one of the
>>>> reasons good numpy support is tricky to get right--the compiler can
>>>> optimize stuff on the stack like crazy, but the same can't be said of
>>>> indirect references. I'm fine with sticking all temps in the closure
>>>> for now if that gets us up and running faster, and optimizing things
>>>> later.
>>>>
>>>>>>> I'm not sure about manage_ref flag I guess that temps with
>>>>>>> manage_ref=False shouldn't be allocated on closure (am I right?)
>>>>>>
>>>>>> "manage_ref=False" (for a Python object value) just means that the
>>>>>> reference is either borrowed or will be stolen at some point. I'm not 
>>>>>> sure
>>>>>> that stealing the reference may not happen *after* a yield, but I guess
>>>>>> that would produce other problems if the reference isn't kept alive in
>>>>>> another way. Nothing to worry about now, carefully crafted tests will
>>>>>> eventually catch that.
>>>>>>
>>>>>> In any case, all live temps (Python refs, borrowed refs and C values) may
>>>>>> be needed after the yield, so they must *all* end up in the closure.
>>>>
>>>> And of course we need to make all live reference are properly
>>>> deallocated when the closure is GC'd as well.
>>>>
>>>> - Robert
>>>> _______________________________________________
>>>> Cython-dev mailing list
>>>> Cython-dev@codespeak.net
>>>> http://codespeak.net/mailman/listinfo/cython-dev
>>>>
>>>
>>> YieldExprNode can check what temps are currently used and
>>> save/restore, and declare them on demand in closure.
>>> Sounds not bad too. This is not a problem.
>>>
>>> I created now I create AbstractGeneratorClass and make it base for closure.
>>> I  want to bind tp_iter directly to my C functions, I found this way,
>>> but it's a little bit trickey...
>>>
>>> entry = klass.declare_var('__iter__', PyrexTypes.py_object_type, pos,
>>> visibility='extern')
>>> entry.func_cname = 'PyObject_SelfIter'
>>>
>>>
>>> I'm now stuck with generator body and function that returns genertor:
>>>
>>> DefNode should be translated into generator body:
>>>
>>> first I wanted to make it PyObject (*)(PyObject *closure, PyObject
>>> *value, int is_exception)
>>> But it seems to be hard now. And I'm thinking to put value and
>>> is_exception into AbstractGenerator class. And let generator body
>>> C-function accept original PyFunction arguments (PyObject *args,
>>> PyObject *kwargs)
>>>
>>> Also GeneratorWrapperNode should create C-function that creates
>>> closure and returns it. That's not very clean should that be attribute
>>> of DefNode, or should it subclass DefNode. So I'm thinking to try it
>>> in a dirty way.
>>>
>>> --
>>> vitja.
>>>
>>
>> Btw it would be very nice to split DefNode into smaller parts. Not sure how.
>>
>> --
>> vitja.
>>
>
> First success
>
> vi...@vitja-laptop:~/tmp/cython-devel-hg/zzz$ python
> Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56)
> [GCC 4.4.5] on linux2
> Type "help", "copyright", "credits" or "license" for more information.
>>>> import generator
>>>> x = generator.foo([1,2,3])
>>>> next(x)
> 1
>>>> next(x)
> 2
>>>> next(x)
> 3
>>>> next(x)
> Traceback (most recent call last):
>  File "<stdin>", line 1, in <module>
>  File "generator.pyx", line 1, in generator.foo (generator.c:518)
>    def foo(x):
> StopIteration
>>>>
>
>
>
> --
> vitja.
>

Please review this patch. It's not yet finished, and mostly doesn't work.
But you can take a look at patch and generated code.

 - Temps are saved/restored/allocated inside YieldExprNode using
helper ClosureTempAllocator
 - PyxGenerator methods are defined via declare_var
 - Send,  __next__(), __iter__() are implemented while close(), throw() aren't
 - YieldExprNode doesn't handle exceptions

 - I don't know how to make refnanny work, should refnanny context be
stored in closure?
 - Objects can leak now, wouldn't check until refnanny work
 - GeneratorWrapperNode isn't node and is mostly a hack
 - Comprehensions are now handled by OldYieldExprNode
 - regr tests are broken

-- 
vitja.

Attachment: generators-support.patch.gz
Description: GNU Zip compressed data

_______________________________________________
Cython-dev mailing list
Cython-dev@codespeak.net
http://codespeak.net/mailman/listinfo/cython-dev

Reply via email to