hi,
what is the proper way (fast, robust, surviving Pd api changes) for an external to send arbitrarily long messages, if these messages contain a mix of data acquired through inlets and data stored in an object?
Say, there is a function, declared as a method of an external class, which is going to pass such a message to an 'outlet' call. Should this function:
1. copy data to the main stack, i.e. use a buffer in the local memory space of a function;
2. handle a private stack ala binbuf_eval();
3. call binbuf_eval();
4. allocate with getbytes() in every call (as pack/unpack_anything() does);
5. use a pre-allocated buffer stored in an object's memory space, after making sure the call is not reentrant (otherwise choose one of 1-4, or do nothing)?
I am asking this in the context of porting max classes to Pd. I suspect, that most of the originals use method no 1. This is not a problem in max, in which messages cannot exceed a 256-atom limit. But even in max this limit might get lifted some day...
Krzysztof
hi,
perhaps all the more experienced Pd coders did not notice this thread (or my question was badly posed)? Anyway, I would like to try once again, this time with some measurement data (not very hard data yet)...
...on my system, using method 5 as a reference, on average:
. method 1 performs 2.5 times slower,
. method 4 is 4.25 times slower and has nasty peaks.
For each method there are two objects in my test patch: one outputs a message stored internally (no copying cost in case of method 5), the other appends its internal message to a message passed from the first object.
Whoever likes to try the same (lame) test, and make further experiments, might grab the newest cyclone snapshot, wherein there is a 'testmess' class and a testmess-test.pd test patch.
Krzysztof
Krzysztof Czaja wrote: ...
what is the proper way (fast, robust, surviving Pd api changes) for an external to send arbitrarily long messages, if these messages contain a mix of data acquired through inlets and data stored in an object?
...
- copy data to the main stack, i.e. use a buffer in the local
memory space of a function;
...
- allocate with getbytes() in every call
...
- use a pre-allocated buffer stored in an object's memory space,
i think i've always used method 5 . .. . because of nasty crashes with dynamic allocation... which i didn't want to debug...
i didn't know it was also the best as regards to performances :-)
yves/
Krzysztof Czaja wrote:
hi,
perhaps all the more experienced Pd coders did not notice this thread (or my question was badly posed)? Anyway, I would like to try once again, this time with some measurement data (not very hard data yet)...
...on my system, using method 5 as a reference, on average:
. method 1 performs 2.5 times slower,
. method 4 is 4.25 times slower and has nasty peaks.
For each method there are two objects in my test patch: one outputs a message stored internally (no copying cost in case of method 5), the other appends its internal message to a message passed from the first object.
Whoever likes to try the same (lame) test, and make further experiments, might grab the newest cyclone snapshot, wherein there is a 'testmess' class and a testmess-test.pd test patch.
Krzysztof
Krzysztof Czaja wrote: ...
what is the proper way (fast, robust, surviving Pd api changes) for an external to send arbitrarily long messages, if these messages contain a mix of data acquired through inlets and data stored in an object?
...
- copy data to the main stack, i.e. use a buffer in the local
memory space of a function;
...
- allocate with getbytes() in every call
...
- use a pre-allocated buffer stored in an object's memory space,
PD-dev mailing list PD-dev@iem.kug.ac.at http://iem.kug.ac.at/cgi-bin/mailman/listinfo/pd-dev
On Thu, 6 Jun 2002, Krzysztof Czaja wrote:
hi,
perhaps all the more experienced Pd coders did not notice this thread (or my question was badly posed)?
Me, for my part, I was to lazy to think about it :)
Anyway, I would like to try once again, this time with some measurement data (not very hard data yet)...
...on my system, using method 5 as a reference, on average:
. method 1 performs 2.5 times slower,
. method 4 is 4.25 times slower and has nasty peaks.
Frequent memory allocation should always be avoided, so method 5 is the best way to go. If your buffer gets to small, reallocate its memory increasing it by an appropriate size, that has to be done only in a few cases, so most of the time you will get away with the best of both worlds, no memory allocation and dynamic space,
Guenter
thanks Guenter,
indeed, I would happily choose pre-allocation method -- this is a default in 'testmess' class. But I need to sort out two things:
. how to handle reentrant calls
. whether/when to collect the garbage.
My second favourite is the stack method, which, unfortunately, requires choosing a more or less arbitrary limit for a passable message length.
Then, there is a mixed solution of choosing the best method each time the function is called, depending on current state of an object and on input data size. But I am afraid it is too complicated to maintain separately for every class.
Btw, I have performed a few more tests, this time with randomized message lengths, and using another, gcc-2.96-based system. The results for [testmess heap] are much worse than I expected...
Krzysztof
? wrote: ...
Frequent memory allocation should always be avoided, so method 5 is the best way to go. If your buffer gets to small, reallocate its memory increasing it by an appropriate size, that has to be done only in a few cases, so most of the time you will get away with the best of both worlds, no memory allocation and dynamic space,
On Fri, 7 Jun 2002, Krzysztof Czaja wrote:
indeed, I would happily choose pre-allocation method -- this is a default in 'testmess' class. But I need to sort out two things:
. how to handle reentrant calls
I see, if I understand it right the fear is you get called again while your old message might still be useful in some context ? i am not sure if this can happen within pd, because the messages are delivered directly from within the method, and recursion on this level isn't possible ?
. whether/when to collect the garbage.
My second favourite is the stack method, which, unfortunately, requires choosing a more or less arbitrary limit for a passable message length.
maybe the easiest way to do it ...
Then, there is a mixed solution of choosing the best method each time the function is called, depending on current state of an object and on input data size. But I am afraid it is too complicated to maintain separately for every class.
Btw, I have performed a few more tests, this time with randomized message lengths, and using another, gcc-2.96-based system. The results for [testmess heap] are much worse than I expected..
might be an issue of the processor cache ?
Guenter
thanks again Guenter,
the fear is of having an output message which is passed from my object to a couple of targets, where the first target would then pass some data back to my object, thus overriding the buffer. I would like to safeguard against a possibility of passing a message from an overwritten buffer to the other destination.
Of course, blocking reentrant calls (``doing nothing''), is one of the options...
Krzysztof
? wrote: ...
I see, if I understand it right the fear is you get called again while your old message might still be useful in some context ? i am not sure if this can happen within pd, because the messages are delivered directly from within the method, and recursion on this level isn't possible ?
hi again,
in order to somehow put an end to this boring thread (who said dev list has to be interesting?), let me report my latest findings.
After performing a number of tests in max 4.0 (also checking the few examples in its sdk) I am pretty sure all the simple classes in max use a preallocated, fixed-sized output buffer, with no special care for reentrancy. Since this buffer never grows, reentrant calls are `safe'.
Anyway, I finally decided to adopt a slightly mixed scheme (because I intend to handle messages of any size). This is a comment snippet from my code:
Usually a preallocation method is used, except in special cases of: 1) reentrant output request, or 2) an output request which would cause resizing to more than MAXSIZE (no such limit for a 'set' message). In both special cases, a temporary output buffer is allocated. A separately preallocated output buffer is not used, thus avoiding memcpying of the stored message (a small performance gain when the preallocation method is chosen). Instead, self-invoked 'set' messages are postponed, using an auxiliary buffer.
If you see any serious flaw in this method, please let me know...
Krzysztof
Hi all,
I can't think of a better way to do this. It's too bad it's so cumbersome, however!
cheers Miller
On Mon, Jun 10, 2002 at 04:47:58PM +0200, Krzysztof Czaja wrote:
hi again,
in order to somehow put an end to this boring thread (who said dev list has to be interesting?), let me report my latest findings.
After performing a number of tests in max 4.0 (also checking the few examples in its sdk) I am pretty sure all the simple classes in max use a preallocated, fixed-sized output buffer, with no special care for reentrancy. Since this buffer never grows, reentrant calls are `safe'.
Anyway, I finally decided to adopt a slightly mixed scheme (because I intend to handle messages of any size). This is a comment snippet from my code:
Usually a preallocation method is used, except in special cases of: 1) reentrant output request, or 2) an output request which would cause resizing to more than MAXSIZE (no such limit for a 'set' message). In both special cases, a temporary output buffer is allocated. A separately preallocated output buffer is not used, thus avoiding memcpying of the stored message (a small performance gain when the preallocation method is chosen). Instead, self-invoked 'set' messages are postponed, using an auxiliary buffer.
If you see any serious flaw in this method, please let me know...
Krzysztof
PD-dev mailing list PD-dev@iem.kug.ac.at http://iem.kug.ac.at/cgi-bin/mailman/listinfo/pd-dev