
As I can see a question similar to mine was already raised here: http://www.haskell.org/pipermail/glasgow-haskell-users/2004-February/006305.... I also encountered the elegance that Haskell's laziness provides for signal processing. I use lists for the signals. With help of Haskore I put together a small song and it needed 5 minutes for rendering the quite simple music of about 30 seconds. Don't care about the exact numbers but I find it rather slow. What is the most promising strategy to speed up the computation without loosing the elegance of Haskell's lists? 1. Upgrading GHC from 6.0 to the current version? 2. Use of simplification rules? How can I find out what new rules may be useful? The several dumps that GHC can generate are a bit hard to understand. :-( Is there some more detailed information about what rules GHC already applies than what one can get with -ddump-simpl-stats and -ddump-rules? 3. More strictness? Is a custom list structure with strict entries and maybe with only one constructor (thus always infinite) a considerable alternative? This would disallow the use of lots of functions that deal with lists, including State monad functions. 4. A list of arrays? Then signal processes with feedback like an echo are much more difficult.

On Sat, Apr 03, 2004 at 07:04:18PM +0200, Henning Thielemann wrote:
What is the most promising strategy to speed up the computation without loosing the elegance of Haskell's lists? 1. Upgrading GHC from 6.0 to the current version? 2. Use of simplification rules? How can I find out what new rules may be useful? The several dumps that GHC can generate are a bit hard to understand. :-( Is there some more detailed information about what rules GHC already applies than what one can get with -ddump-simpl-stats and -ddump-rules? 3. More strictness? Is a custom list structure with strict entries and maybe with only one constructor (thus always infinite) a considerable alternative? This would disallow the use of lots of functions that deal with lists, including State monad functions. 4. A list of arrays? Then signal processes with feedback like an echo are much more difficult.
Try using profiling to see where exactly your time is being spent, and then perhaps posting back here with the problematic function. Also, of course, looking at scaling is key, make you know the scaling of each function (i.e. how many times it gets called), and that there isn't anything you can do to fix the scaling. Lists are slow, and laziness is slow, but on the other hand, computers are fast, so before trying any of 1-4 (all of which seem likely to uglify your code a bit) I'd try to see if you can fix up the algorithms in your code to be more efficient. I assume that if your highest priority was writing blindingly fast code, you'd be writing in C. I guess one thing you could try (if you haven't already done so) is writing a simple program that just reads and writes and does nothing in between (except convert to a lazy list), to see how slow that is. Is laziness really an important issue? Are you interested in it mostly just for memory consumption reasons, to avoid holding an entire file in memory, or are you perhaps thinking to make the code work with real-time input? If you decide you need something faster (but still pretty) and don't need laziness and can fit your data in memory, I think an infinite array might be the way to go. You could pretty easily create a wrapper around a UArray or a ForeignPtr which would pad it with zeros in both plus and minus infinity directions. Then you could also create a "time offset" function that shifts an array by a given amount (without making a copy), and could probably do most of what you'd like to do. I imagine something like data InfiniteArray = IA !(ForeignPtr Int16) !Int !Int (!) :: InfiniteArray -> Int -> Int16 (IA for_ptr start_index end_index the_offset) ! i | i < start_index = 0 | i > end_index = 0 | otherwise = unsafePerformIO $ withForeignPtr for_ptr $ \p -> peekElemOff p (the_offset + i) createIA :: Int -> (Ptr Word16 -> IO ()) -> InfiniteArray createIA length write_ptr = unsafePerformIO $ do fp <- mallocForeignPtr length withForeignPtr fp $ \p -> write_ptr p return $ IA fp 0 (l-1) 0 offsetIA :: Int -> InfiniteArray -> InfiniteArray offsetIA offset_by (IA start_index end_index the_offset) = IA (start_index + offset_by) (end_index + offset_by) (the_offset - offset_by) Note that I only used ForeignPtr's because I'm familiar with them, and haven't used UArrays much. Unless you need to use FFI, you're probably better off with UArrays. On the other hand, with ForeignPtr's you leave yourself open to the possibility of calling an FFT program through the FFI, and you definitely *don't* want to write an FFT in haskell. -- David Roundy http://www.abridgegame.org

On Sat, 3 Apr 2004, David Roundy wrote:
On Sat, Apr 03, 2004 at 07:04:18PM +0200, Henning Thielemann wrote:
What is the most promising strategy to speed up the computation without loosing the elegance of Haskell's lists? 1. Upgrading GHC from 6.0 to the current version? 2. Use of simplification rules? How can I find out what new rules may be useful? The several dumps that GHC can generate are a bit hard to understand. :-( Is there some more detailed information about what rules GHC already applies than what one can get with -ddump-simpl-stats and -ddump-rules? 3. More strictness? Is a custom list structure with strict entries and maybe with only one constructor (thus always infinite) a considerable alternative? This would disallow the use of lots of functions that deal with lists, including State monad functions. 4. A list of arrays? Then signal processes with feedback like an echo are much more difficult.
Lists are slow, and laziness is slow, but on the other hand, computers are fast, so before trying any of 1-4 (all of which seem likely to uglify your code a bit)
For 1. a change wouldn't be necessary at all, for 2. the rules can be kept away from the actual implementation. Thus these variants would uglify the code only minimally.
I assume that if your highest priority was writing blindingly fast code, you'd be writing in C.
No, not _that_ ugly! :-) I have already coded some of the algorithms in Motorola DSP 56000 assembly language.
I guess one thing you could try (if you haven't already done so) is writing a simple program that just reads and writes and does nothing in between (except convert to a lazy list), to see how slow that is.
I have attached such an example together with the generated profile. It generates two signals: 1. 100000 zero samples, 2. 100000 samples of a saw oscillator with exponentially decaying frequency. Together it needs 1.0 seconds on my machine with 1.6 GHz which is very slow compared to the computational effort. The conversion from floating point numbers to 16 bit signed integers is quite cumbersome, maybe you know of a better one. Btw. is there a version of 'properFraction' that evaluates 'properFraction (-1.5)' to (-2,0.5) rather than (-1,-0.5) ?
Is laziness really an important issue? Are you interested in it mostly just for memory consumption reasons, to avoid holding an entire file in memory, or are you perhaps thinking to make the code work with real-time input?
I don't want to exclude real-time processing from the beginning. It's an unfortunate connection that laziness is perfectly suited for real-time processing but is much too slow for it. ;-)
On the other hand, with ForeignPtr's you leave yourself open to the possibility of calling an FFT program through the FFI, and you definitely *don't* want to write an FFT in haskell.
It's already done: http://haskelldsp.sourceforge.net/doc/Numeric.Transform.Fourier.FFT.html :-)

The idea of using a general purpose programming language for making music as provided by Haskore is very attractive to me. Especially Haskell seems to be a good choice for this task, since the lazy evaluation in principle allows an immediate play back without the necessity to build the complete song data structure in advance. The concise syntax that is possible because of the type unification system is also very nice. So I played around a bit with Haskore and wondered if there is some more activity on it. I've found several related projects like: http://www.mpi-sb.mpg.de/~ratschan/autotrack/ (Autotrack) http://meltin.net/hacks/haskell/ (Haschorus) http://haskell.cs.yale.edu/edsl/music.htm (EDSL) As far as I understand all of them address a simplified usage of Haskore. Are there also people interested in making Haskore more flexible and more logical?
participants (2)
-
David Roundy
-
Henning Thielemann