Dear list, In this forum topic http://electro-music.com/forum/viewtopic.php?t=29358there is some talk about the time that a operation takes as compared to how it affects "now". As I said there; we may want to calculate a lot of numbers before starting synthesis (say in the case of a algorithmic score) and only start synthesis after we have those numbers. Right now you can't. You can tell ChucK to calculate those numbers, advance time by some amount estimated to be about as long as our CPU takes to do that and only start synthesis (by connecting to the dac) after that, but this will depend on a good estimate, which will of course differ with the CPU we may be on. Aside from this question there is the matter of how long a operation takes in a "strongly timed" language. In the past I have -unsuccessfully- tried to benchmark different versions of a operation using "now". We have a lot of ways of dealing with time, there are probably few languages that deal with time in as much detail as ChucK does but we have no way at all of dealing with time as registered by the clock next to the programmer. There is a paradox here; I'd like to be able to advance time by exactly as much time as was taken (by the cpu) since the last advancing of time; the problem with that is that determining this might itself take quite a few CPU cycles while this is the sort of operation we would use when trying to do things as quickly as possible. Another question is that we can't yield to the UGen graph if need be. A loop of just repeated yielding will stop the Ugen calculations. I have no solutions to this but thought this forum topic at least gave (yielded?) a new look at the question. For what it's worth, Kas.
On Thu, Oct 9, 2008 at 5:04 PM, Kassen
Dear list,
In this forum topic
ah! worlds are colliding!
Aside from this question there is the matter of how long a operation takes in a "strongly timed" language. In the past I have -unsuccessfully- tried to benchmark different versions of a operation using "now". We have a lot of ways of dealing with time, there are probably few languages that deal with time in as much detail as ChucK does but we have no way at all of dealing with time as registered by the clock next to the programmer.
ChucK could probably use the equivalent of Pd's "realtime" object. I suggest calling it "nowreally"!
There is a paradox here; I'd like to be able to advance time by exactly as much time as was taken (by the cpu) since the last advancing of time; the problem with that is that determining this might itself take quite a few CPU cycles while this is the sort of operation we would use when trying to do things as quickly as possible. Another question is that we can't yield to the UGen graph if need be. A loop of just repeated yielding will stop the Ugen calculations.
This is because if all other shreds have tried to advance time but one shred is just yielding and not advancing time, ChucK VM will just keep running that same shred until it decides time can keep moving. I can't really parse the first sentence in this paragraph though. :-)
Stephen; ChucK could probably use the equivalent of Pd's "realtime" object. I
suggest calling it "nowreally"!
realness does seem to depend on the perspective.... I
can't really parse the first sentence in this paragraph though. :-)
In my defence; we are dealing with several types of "now" and at least a few types of "time". It's my fault but famous philosophers demonstrated that talking about languages is hard in ways that need not be solvable without inventing another language. Fortunately you are a object of type "person" and I gather those are amazingly tolerant for faulty input. ; ¬) Cheers, Kas.
On Thu, Oct 9, 2008 at 7:13 PM, Kassen
Stephen;
ChucK could probably use the equivalent of Pd's "realtime" object. I suggest calling it "nowreally"!
realness does seem to depend on the perspective....
I can't really parse the first sentence in this paragraph though. :-)
In my defence; we are dealing with several types of "now" and at least a few types of "time". It's my fault but famous philosophers demonstrated that talking about languages is hard in ways that need not be solvable without inventing another language.
Sure, but this is a common thing to deal with in audio languages. Pd calls it "logical time" and "real time", which I think is the correct terminology. So as for the code I posted in the forum, with a way of determining 'real' now I could have done, // one million sqrts 0 => float answer; for (1=>int i; i<=1000000; i++) { answer+sqrt(i) => answer; if ((realnow - now) > 3::ms) 3::ms => now; } Btw, is this actually what you were asking or am I way off? Steve
Stephen
Sure, but this is a common thing to deal with in audio languages.
I can imagine! talking about "talking" is inherently hard. Pd
calls it "logical time" and "real time", which I think is the correct terminology.
Sounds quite sensible to me.
So as for the code I posted in the forum, with a way of determining 'real' now I could have done,
// one million sqrts 0 => float answer; for (1=>int i; i<=1000000; i++) { answer+sqrt(i) => answer; if ((realnow - now) > 3::ms) 3::ms => now; }
Btw, is this actually what you were asking or am I way off?
No, that seems about right, in fact I'd say "realnow => now" ought to do, even. That just leaves the question of where we'd get a "realnow". If we'd get a "realnow" as a proper language abstraction that would deal with a lot though I'd say "realnow" for the HD may not be the same as for the CPU. To put it in other words; ChucK lets us deal with a VM which is very pleasant as the VM is much simpler then a actual computer but the limits of the host computer may be relevant for what we'd like to tell the VM. Yours, kas.
On Thu, Oct 9, 2008 at 7:44 PM, Kassen
No, that seems about right, in fact I'd say "realnow => now" ought to do, even. That just leaves the question of where we'd get a "realnow". If we'd get a "realnow" as a proper language abstraction that would deal with a lot though I'd say "realnow" for the HD may not be the same as for the CPU.
To put it in other words; ChucK lets us deal with a VM which is very pleasant as the VM is much simpler then a actual computer but the limits of the host computer may be relevant for what we'd like to tell the VM.
I like "realnow => now" very much. That's a great syntax for it. ;-) I'm sure some kind of pun could be used here, but it's not coming to me... "nowreally" is the best I could come up with. Steve
Stephen
I like "realnow => now" very much. That's a great syntax for it. ;-)
I'm sure some kind of pun could be used here, but it's not coming to me... "nowreally" is the best I could come up with.
I like it too, but if anything ever did this needs Ge and he has a a affinity for and a talent in that field. Barring better ideas and terms I'm behind this. Yours, Kas.
On Thu, Oct 9, 2008 at 5:04 PM, Kassen
In this forum topic http://electro-music.com/forum/viewtopic.php?t=29358 there is some talk about the time that a operation takes as compared to how it affects "now". As I said there; we may want to calculate a lot of numbers before starting synthesis (say in the case of a algorithmic score) and only start synthesis after we have those numbers. Right now you can't. You can tell ChucK to calculate those numbers, advance time by some amount estimated to be about as long as our CPU takes to do that and only start synthesis (by connecting to the dac) after that, but this will depend on a good estimate, which will of course differ with the CPU we may be on.
Aside from this question there is the matter of how long a operation takes in a "strongly timed" language. In the past I have -unsuccessfully- tried to benchmark different versions of a operation using "now". We have a lot of ways of dealing with time, there are probably few languages that deal with time in as much detail as ChucK does but we have no way at all of dealing with time as registered by the clock next to the programmer.
There is a paradox here; I'd like to be able to advance time by exactly as much time as was taken (by the cpu) since the last advancing of time; the problem with that is that determining this might itself take quite a few CPU cycles while this is the sort of operation we would use when trying to do things as quickly as possible. Another question is that we can't yield to the UGen graph if need be. A loop of just repeated yielding will stop the Ugen calculations.
I have no solutions to this but thought this forum topic at least gave (yielded?) a new look at the question.
With code like: while(notdone) { dowork(); pause_dur => now; } tweaking pause_dur until the computationally heavy code runs without skipping is the same as benchmarking it to figure out how long it takes to execute, and it's safe. I don't think system calls, even ones like reading files, should take a shred out of the shreduler until they finish, though I admit that's personal preference. Deterministic parallel code like ChucK's is hard to come by. But code like this: me =< now; // step outside time doallwork(); me => now; // step back just can't happen. Allowing shreds to step outside time would solve all the problems brought up in the thread, but it would weaken some of the data consistency guarantees ChucK's cooperative multishredding gives you. Even if ChucK could interleave execution of your Neo shred with that of those still enslaved by the virtual machines, Neo would have to be frequently interrupted in the middle of whatever he was doing to prevent audio underflows. And since he doesn't know when he'll be interrupted, his data won't necessarily be consistent. If it were a local variable in a for-loop, fine; but what if it were a variable global to the file, or a static variable in a class used by other shreds? ChucK code is written with the assumption that if doesn't let time pass, no data can change beneath its feet. My recommendation: use multiple ChucKs with message-passing (OSC, whatever). I hear Prof. Cook does it, and he seems like an all right guy. --- By the way, I'm sorry, but I still can't figure out what this would be expected to do: realnow => now; -- Tom Lieber http://AllTom.com/
On Thu, Oct 9, 2008 at 8:55 PM, Tom Lieber
With code like:
while(notdone) { dowork(); pause_dur => now; }
tweaking pause_dur until the computationally heavy code runs without skipping is the same as benchmarking it to figure out how long it takes to execute, and it's safe.
I don't think system calls, even ones like reading files, should take a shred out of the shreduler until they finish, though I admit that's personal preference. Deterministic parallel code like ChucK's is hard to come by.
But code like this:
me =< now; // step outside time doallwork(); me => now; // step back
just can't happen.
Allowing shreds to step outside time would solve all the problems brought up in the thread, but it would weaken some of the data consistency guarantees ChucK's cooperative multishredding gives you. Even if ChucK could interleave execution of your Neo shred with that of those still enslaved by the virtual machines, Neo would have to be frequently interrupted in the middle of whatever he was doing to prevent audio underflows. And since he doesn't know when he'll be interrupted, his data won't necessarily be consistent.
Hm, are you talking about introducing "real" parallelism? (i.e., pthreads) I didn't really mean to imply that in anything I wrote, at least. I agree that doing it properly would be difficult and error prone. Probably the "out of time" shred would need to be forced not to access any common objects, at the very least, and communicate by other means than global variables. (this would be equivalent to OSC, as you mention at the bottom.)
If it were a local variable in a for-loop, fine; but what if it were a variable global to the file, or a static variable in a class used by other shreds? ChucK code is written with the assumption that if doesn't let time pass, no data can change beneath its feet.
My recommendation: use multiple ChucKs with message-passing (OSC, whatever). I hear Prof. Cook does it, and he seems like an all right guy.
---
By the way, I'm sorry, but I still can't figure out what this would be expected to do:
realnow => now;
The problem that was brought up on the forum was how to do a bunch of intensive computation before starting the audio synthesis running, without causing problems. I think I took the liberty of generalizing the question into how can we do a lot of computation _anywhere_ in a chuck program without stalling the other shreds and causing havoc with the dac. My suggestion was to advance time throughout the lengthy computation, to make sure every other shred can get a chance to do its stuff. Kassen noticed that we have no way of knowing at what point during the lengthy computation we should advance time, without making a guess as to how long the computation will take. The 'realnow' suggestion is to make it possible to tell what time it is in real time. In logical time, a bunch of computation takes no time at all, but of course in the real world time is passing. So looking at the difference between realnow and now would tell you how much time you've used doing computations. In principle at the immediate beginning of a function, realnow and now are equal. After a few instructions, realnow has advanced, but now is still the same. So chucking realnow to now makes logical time advance to the state of real time. In other words, if a function takes 3 ms to complete, you could make sure not to interrupt other shreds by, while (...) { longfunction(); realnow => now; } This would ensure that logical time advances during the computation, while not forcing you to guess the actual time that longfunction() takes to execute. Note that none of this means changing anything in the shreduler. And yes, using multiple chucks with OSC is something else I suggested on the forum. You could even use another more optimized language to do the tough computations and pass back the results. OSC is great because it makes it easy to introduce inter-process procedure calls. Steve
On Thu, Oct 9, 2008 at 9:31 PM, Stephen Sinclair
On Thu, Oct 9, 2008 at 8:55 PM, Tom Lieber
wrote: With code like:
while(notdone) { dowork(); pause_dur => now; }
tweaking pause_dur until the computationally heavy code runs without skipping is the same as benchmarking it to figure out how long it takes to execute, and it's safe.
I don't think system calls, even ones like reading files, should take a shred out of the shreduler until they finish, though I admit that's personal preference. Deterministic parallel code like ChucK's is hard to come by.
But code like this:
me =< now; // step outside time doallwork(); me => now; // step back
just can't happen.
Allowing shreds to step outside time would solve all the problems brought up in the thread, but it would weaken some of the data consistency guarantees ChucK's cooperative multishredding gives you. Even if ChucK could interleave execution of your Neo shred with that of those still enslaved by the virtual machines, Neo would have to be frequently interrupted in the middle of whatever he was doing to prevent audio underflows. And since he doesn't know when he'll be interrupted, his data won't necessarily be consistent.
Hm, are you talking about introducing "real" parallelism? (i.e., pthreads) I didn't really mean to imply that in anything I wrote, at least. I agree that doing it properly would be difficult and error prone. Probably the "out of time" shred would need to be forced not to access any common objects, at the very least, and communicate by other means than global variables. (this would be equivalent to OSC, as you mention at the bottom.)
No, I am describing what it means for a single shred to execute "in the background, for as long as it takes to finish." This is moving from cooperative scheduling (shreds yielding time when they are ready) to preemptive scheduling (shreds forced to yield time at the shreduler's convenience), which is where concurrent modification happens. Not allowing such shreds to access objects outside of its scope would solve the concurrency problems, though that seems extremely limiting.
By the way, I'm sorry, but I still can't figure out what this would be expected to do:
realnow => now; ... In other words, if a function takes 3 ms to complete, you could make sure not to interrupt other shreds by,
while (...) { longfunction(); realnow => now; }
This would ensure that logical time advances during the computation, while not forcing you to guess the actual time that longfunction() takes to execute.
Note that none of this means changing anything in the shreduler.
That wouldn't help at all, though. ChucK has to execute longfunction() to completion (holding up every other shred during that time) before it gets to "realnow => now;". -- Tom Lieber http://AllTom.com/
On Thu, Oct 9, 2008 at 10:09 PM, Tom Lieber
In other words, if a function takes 3 ms to complete, you could make sure not to interrupt other shreds by,
while (...) { longfunction(); realnow => now; }
This would ensure that logical time advances during the computation, while not forcing you to guess the actual time that longfunction() takes to execute.
Note that none of this means changing anything in the shreduler.
That wouldn't help at all, though. ChucK has to execute longfunction() to completion (holding up every other shred during that time) before it gets to "realnow => now;".
Sorry, I meant to imply that longfunction() is a short part of a longer, iterative computation. In other words, all meant to say is, "move time along in the middle of your long computation". Steve
On Thu, Oct 9, 2008 at 10:49 PM, Stephen Sinclair
On Thu, Oct 9, 2008 at 10:09 PM, Tom Lieber
wrote: In other words, if a function takes 3 ms to complete, you could make sure not to interrupt other shreds by,
while (...) { longfunction(); realnow => now; }
This would ensure that logical time advances during the computation, while not forcing you to guess the actual time that longfunction() takes to execute.
Note that none of this means changing anything in the shreduler.
That wouldn't help at all, though. ChucK has to execute longfunction() to completion (holding up every other shred during that time) before it gets to "realnow => now;".
Sorry, I meant to imply that longfunction() is a short part of a longer, iterative computation. In other words, all meant to say is, "move time along in the middle of your long computation".
Okay, that makes more sense. Still... that only helps with implementations of longfunction() that take much less than a sample on average to complete. If it's about a sample, you may as well replace it with 1::samp => now;. If it's more than a sample, it will cause underruns anyway. -- Tom Lieber http://AllTom.com/
On Thu, Oct 9, 2008 at 11:15 PM, Tom Lieber
On Thu, Oct 9, 2008 at 10:49 PM, Stephen Sinclair
wrote: On Thu, Oct 9, 2008 at 10:09 PM, Tom Lieber
wrote: In other words, if a function takes 3 ms to complete, you could make sure not to interrupt other shreds by,
while (...) { longfunction(); realnow => now; }
This would ensure that logical time advances during the computation, while not forcing you to guess the actual time that longfunction() takes to execute.
Note that none of this means changing anything in the shreduler.
That wouldn't help at all, though. ChucK has to execute longfunction() to completion (holding up every other shred during that time) before it gets to "realnow => now;".
Sorry, I meant to imply that longfunction() is a short part of a longer, iterative computation. In other words, all meant to say is, "move time along in the middle of your long computation".
Okay, that makes more sense. Still... that only helps with implementations of longfunction() that take much less than a sample on average to complete. If it's about a sample, you may as well replace it with 1::samp => now;. If it's more than a sample, it will cause underruns anyway.
That's true for the audio buffer size, not 1 sample. Usually the time to calculate an audio buffer is less than the time than to play an audio buffer, which is what allows it to synthesize sound in real time. There is always a bit of time left over after calculating the audio buffer to run a bit of chuck VM code. (In fact the ChucK VM is run after every sample, but it just means that "extra time" is interleaved into the buffer calculations.) That's why my initial example on the forum just uses 3::ms. It's generally more efficient to advance a few ::ms rather than 1 sample at a time. Kassen was suggesting that it would be better to know the actual time it takes for the code to run, instead of using a constant duration like that. Steve
Stephen;
That's true for the audio buffer size, not 1 sample. Usually the time to calculate an audio buffer is less than the time than to play an audio buffer, which is what allows it to synthesize sound in real time.
I agree. We need to keep in mind that all of the "now", advancing time and concurrency syntax is just about the order in which things get executed by a single CPU. it's not some magical well of CPU power; it all depends on "having steam to spare". What we are actually pretending to define, having operations that -themselves take no time at all, is impossible, the whole illusion depends on this buffer and indeed it's a very pleasant illusion. In a way you could say that what we are after here is a method of dealing with the reality that's below this illusion, the reality of finite resources, in a way that's still syntactically coherent with the rest. I'm not even sure this is possible because of factors like the fact that figuring out how much CPU time a certain operation takes also takes CPU time. There is a conflict between how ChucK will never let go of "now" and how we'd ideally prefer audio not to break up. When something has to give (like when we tell ChucK we instantly want to know ten million square roots) it's the audio that gives. It's possible to yield to other shreds, and if you do the shreds will take just what they need and give the CPU back, this is clearly different from advancing time. I suppose that what I'm looking for is a way to yield to the Ugen graph,( which is the strongest link between ChucK's time and the stuff on my clock), give it the CPU to calculate what it needs to calculate (which may well be nothing right now), then give me the CPU back. Maybe this is going too far; this structure would max out the CPU with some percentage for the Ugens and all of the rest (and no more) for our shred. That would mean that if the OS would like to do anything at all (and modern OS's do...) we're in unhealthy teritory. Perhaps we're simply fantasising about the impossible. Yours, Kas.
On Fri, Oct 10, 2008 at 7:16 AM, Kassen
There is a conflict between how ChucK will never let go of "now" and how we'd ideally prefer audio not to break up. When something has to give (like when we tell ChucK we instantly want to know ten million square roots) it's the audio that gives.
The way every UGen does this is to precompute.
I suppose that what I'm looking for is a way to yield to the Ugen graph,( which is the strongest link between ChucK's time and the stuff on my clock), give it the CPU to calculate what it needs to calculate (which may well be nothing right now), then give me the CPU back.
Yielding to the UGen graph is exactly the same as yielding to the other shreds. The UGens are in lock-step with the shreds for the purposes of strong timing. -- Tom Lieber http://AllTom.com/
Tom Lieber;
Yielding to the UGen graph is exactly the same as yielding to the other shreds. The UGens are in lock-step with the shreds for the purposes of strong timing.
Yes, you are right, of course. Stephen said it much better then I did. Either that or we are after something that's fundamentally impossible. Let me try again; I want a computationally intensive shred to Shredule itself for the moment when the VM "now" again equals how long ChucK has been running. I think that's exactly what Stephen proposed but we could easily be barking up entirely different trees. It's a tricky problem and very tricky to talk about, I now realise (pun intended). Yours, Kas.
On Thu, Oct 9, 2008 at 6:31 PM, Stephen Sinclair
The problem that was brought up on the forum was how to do a bunch of intensive computation before starting the audio synthesis running, without causing problems. I think I took the liberty of generalizing the question into how can we do a lot of computation _anywhere_ in a chuck program without stalling the other shreds and causing havoc with the dac.
Hi, I was the original poster in that forum thread, and I'd just like to chime in with my thoughts. At first I was merely wondering about what will happen with heavy processing loads; I had no specific timing scenario in my mind, but I did suggest the "compute all before audio begins" method, but that certainly isn't all that interests me. Everyone's responses so far have shed some much needed light on the situation, thanks. I REALLY like the idea behind this code: me =< now; // step outside time doallwork(); me => now; // step back but I guess using OSC would be a similar method, wouldn't it. However, (and maybe my chuck newbness will start to show in this question) what if I was working on an algorithm that required the previous audio sample's value before the next audio sample could be calculated, and those calculations were extremely computationally intensive? The physical constraints of the computer simply wouldn't be able to keep audio flowing at audio rates, but would chuck be able to output the sound to an audiofile? If I chuck both this mythological algorithm file and the rec.ck (from the examples) to the same VM, will the audio file output slowly fill with the correct sequence of audio samples (my gut tells me yes)? Hmm, now to go build a Beowulf cluster to OSC a bunch of code to... Thanks again for the wonderful ideas/perspectives/thouhts. -Eric Hedekar
participants (4)
-
Eric Hedekar
-
Kassen
-
Stephen Sinclair
-
Tom Lieber