Previous: Scheduler Up: A Bestiary of Kiwi Objects
Ideally, the Collector would be a vast buffer allowing reading and writing of any sample in the entire piece quickly and efficiently. In practice, limited memory restricts the Collector's behavior. The score is divided into equal sized grains, and all the samples for one grain must be computed before samples for the next grain will be accepted. Within the grain, samples can be accessed in any order. As soon as all the samples for a given grain are computed, the Collector signals the Scheduler, and computations begin for the next grain.
The Collector maintains a buffer large enough to store the samples generated for the current grain. When new samples arrive, they must be summed with the values already in the buffer. Mutual exclusion must be enforced to prevent loss of data. However, there are many cases in which two sets of samples must be added to distinct regions of the buffer. In those cases parallelism can be allowed without damage to the data. One naive solution is to provide a semaphore for every sample. This permits the maximum possible parallelism, but the cost of locking and unlocking a semaphore for every sample outweighs the speed gained from parallelism. The opposite approach would be to protect the entire buffer with a single semaphore. This minimizes semaphore overhead, but totally eliminates parallelism. Kiwi employs a compromise strategy. The buffer is divided into 400 equal fragments of 1000 samples, each with its own semaphore. This provides parallelism while limiting the semaphore overhead.
Comments to walker@shout.net