[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [sc-users] Memory leak in Pattern:render



On May 5, 2009, at 2:37 AM, Tim Walters wrote:

A little bit of isolation testing shows that problem is in ScoreStreamPlayer:makeScore. If you execute these one at a time, it happens on the fourth:

Well of course, that's where it's collecting the events' OSC messages into the Score.

One way to improve this would be to have a ScoreStreamWriter (instead of Player) that would flush messages out to the file incrementally, rather than collecting everything in memory and writing all at once. I think this would be possible because patterns can't go backward in time (while Score allows an event to be added for any timestamp). We would need to account for OSC scheduled in the future (note releases), but that could be handled without too much trouble using a linked list. (PriorityQueue would be the most transparent way, but I've had issues with it when the queue gets large -- I don't trust it for this application.)

It's a variant on the space vs speed problem -- right now the algorithm is very simple and fast, but uses a lot of memory. Actually, doing it incrementally may not be that much slower because it would not have to sort the message list at the end.

BTW... about the pattern... the main point about patterns is they're a mechanism for lazy evaluation (define the calculation up front, but don't do it until later, on demand). So this way of using patterns, by calculating 1024 * 6 * 8 = 49152 numbers eagerly (not lazily), is valid and gets the job done, but it isn't exactly idiomatic.

a = { Pbind(
\dur, Pseq(({ rrand(0.125, 8.0) } ! 1024).normalizeSum * 30, 1),
\freq, Pseq({ rrand(50.0, 4000.0) } ! 1024, 1),
\amp, Pseq({ rrand(-48.dbamp, -12.dbamp) } ! 1024, 1),
\attack, Pseq({ exprand(0.001, 0.2) } ! 1024, 1),
\release, Pseq({ exprand(0.001, 0.2) } ! 1024, 1),
\legato, Pseq({ [exprand(0.1, 1.0), exprand(1.0, 2.0)].choose } ! 1024, 1)
)} ! 8;

If you want exactly 1024 events with random times to fill exactly 30 seconds, the Pseq for dur might be the best way to write it. All the other patterns could be rewritten using Pwhite for rrand and Pexprand for exprand.

a = { Pbind(
\dur, Pseq(({ rrand(0.125, 8.0) } ! 1024).normalizeSum * 30, 1),
\freq, Pwhite(50.0, 4000.0, inf),
\amp, Pwhite(-48.dbamp, -12.dbamp, inf),
\attack, Pexprand(0.001, 0.2), inf),
\release, Pexprand(0.001, 0.2), inf),
// Pswitch1 replicates .choose in your example
\legato, Pswitch1([Pexprand(0.1, 1.0, inf), Pexprand(1.0, 2.0, inf)], Pwhite(0, 1, inf))
)} ! 8;

But, if you don't care about the exact number of events, Pfindur will constrain it to 30 seconds. In that case you could duplicate the pattern, not the function, because Ppar will make separate streams anyway.

a = Pfindur(30, Pbind(
\dur, Pwhite(0.0125, 0.8, inf), // just guessing here on the range
\freq, Pwhite(50.0, 4000.0, inf),
\amp, Pwhite(-48.dbamp, -12.dbamp, inf),
\attack, Pexprand(0.001, 0.2), inf),
\release, Pexprand(0.001, 0.2), inf),
\legato, Pswitch1([Pexprand(0.1, 1.0, inf), Pexprand(1.0, 2.0, inf)], Pwhite(0, 1, inf))
)) ! 8;

That won't help with the memory profile, but maybe helpful for future work.

hjh


: H. James Harkins
.::!:.:.......:.::........:..!.::.::...:..:...:.:.:.:..:

"Come said the Muse,
Sing me a song no poet has yet chanted,
Sing me the universal."  -- Whitman