Reflections on the ICFP 2009 programming contest

Anyone have thoughts to share? I'd love to read others' experiences but there isn't much coming up with searches or on redditt ... I was happiest with the VM I implemented. Sadly, I wasn't able to solve any of the scenarios, but my VM ran damn fast. That didn't seem to matter so much this year as it did two years ago - I think I am still scarred by Endo's DNA .. Anyways, for those who care, the heart of my VM implementation was a monadic fold over the program, with a mutable array representing the machine's memory, all inside ''runSTUArray.'' I used a simple data type to represent the machine: data Machine = Machine { instrs :: [OpCode] , dataMem :: UArray Int Val , outputs :: UArray Int Val , status :: !Int , inputs :: IntMap Val } where ''dataMem'' holds the data memory area. To execute a program, I folded over the ''instrs'' list and updated memory as I went: exec :: Machine -> Input -> Machine exec m@(Machine { instrs = instrs }) inps = let m' = m { inputs = readInputs inps } newMem = runSTUArray $ do mem' <- thaw (dataMem m) foldM (step m') mem' (zip instrs [0..]) I considered ''unsafeThaw'' in the above, but performance didn't hurt me much and it seemed safer. Since I was recording all output I was worried that would break referential integrity. In any case, when the fold was finished I just assigned ''newMem'' to the new machine and moved to the next iteration. ''step'' takes the mutable array and updates it while the program executes. I won't show the whole body, as it just a separate definition for each opcode, just the type: step :: Machine -> STUArray s Int Val -> (OpCode, Addr) -> ST s (STUArray s Int Val) The only downside to this approach was the machine had three mutuable states: memory, output, and status. runSTUArray only wants to return a single array so inside step I had to hold all values in one array. Not a big deal but a little ugly. I kept a diary of my experience over the weekend at http://icfp2009.codeslower.com. I started out with such optimism ... Stil, I'm glad to have done it. Lots of fun! My thanks to the organizers!

I implemented the VM in C, it was pretty obviously geared towards such an implementation and it took all of an hour. Then I interfaced with it via the FFI. Why use just one language when you can use two? :) I wasn't able to make any time on sunday though so didn't end up submitting a final entry which is too bad. this was an interesting one. John -- John Meacham - ⑆repetae.net⑆john⑈ - http://notanumber.net/

I was excited when I read about the VM - I'd imagined all sorts of cool things, like assembler, linker, compiler (for something C-like), maybe even debugger... And what a disappointment it was when I understood that nothing of this kind is needed. On 29 Jun 2009, at 22:55, John Meacham wrote:
I implemented the VM in C, it was pretty obviously geared towards such an implementation and it took all of an hour. Then I interfaced with it via the FFI. Why use just one language when you can use two? :)
I wasn't able to make any time on sunday though so didn't end up submitting a final entry which is too bad. this was an interesting one.
John
-- John Meacham - ⑆repetae.net⑆john⑈ - http://notanumber.net/ _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Mon, Jun 29, 2009 at 11:51:43PM +0400, Miguel Mitrofanov wrote:
I was excited when I read about the VM - I'd imagined all sorts of cool things, like assembler, linker, compiler (for something C-like), maybe even debugger... And what a disappointment it was when I understood that nothing of this kind is needed.
Yeah, those were my thoughts at first too. It took me a couple readings to realize it was simply to give it machine-independence. But perhaps realizing that was part of the challenge :). The fact it didn't have any looping meant that it wasn't even fully turing complete and you probably couldn't speed it up much anyway, it already had an intrinsically short running time. John -- John Meacham - ⑆repetae.net⑆john⑈ - http://notanumber.net/

On 30 Jun 2009, at 00:03, John Meacham wrote:
The fact it didn't have any looping meant that it wasn't even fully turing complete and you probably couldn't speed it up much anyway, it already had an intrinsically short running time.
Exactly! That's an ideal situation, you don't have to speed the program up, you can focus on speeding the development process.

John Meacham 쓴 글:
I implemented the VM in C, it was pretty obviously geared towards such an implementation and it took all of an hour. Then I interfaced with it via the FFI. Why use just one language when you can use two? :)
You could also have used Data.Binary. That's what I did.
I wasn't able to make any time on sunday though so didn't end up submitting a final entry which is too bad. this was an interesting one.
John

We implemented the VM by writing a smallish compiler to C for it in
Haskell. It ran *damn* fast, but we couldn't get rid of some bug that
did not let us run the 4rth task at all, although the others worked
fine :(
2009/6/30 Ahn, Ki Yung
John Meacham 쓴 글:
I implemented the VM in C, it was pretty obviously geared towards such an implementation and it took all of an hour. Then I interfaced with it via the FFI. Why use just one language when you can use two? :)
You could also have used Data.Binary. That's what I did.
I wasn't able to make any time on sunday though so didn't end up submitting a final entry which is too bad. this was an interesting one.
John
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Eugene Kirpichov Web IR developer, market.yandex.ru

The main thing my team implemented in Haskell was a simulator simulator that
took the first two rounds worth of port information and used that to
back into the velocity for each of the satellites. This meant we really only
had to appeal to the actual VM implementation for canonical scoring and to
validate results, which was kind of nice. We implemented the VM itself
independently in python and C to cross-check ourselves and I later put
together a small VM->C compiler once we knew we had the implementation
right.
-Edward Kmett
2009/6/30 Eugene Kirpichov
We implemented the VM by writing a smallish compiler to C for it in Haskell. It ran *damn* fast, but we couldn't get rid of some bug that did not let us run the 4rth task at all, although the others worked fine :(
2009/6/30 Ahn, Ki Yung
: John Meacham 쓴 글:
I implemented the VM in C, it was pretty obviously geared towards such an implementation and it took all of an hour. Then I interfaced with it via the FFI. Why use just one language when you can use two? :)
You could also have used Data.Binary. That's what I did.
I wasn't able to make any time on sunday though so didn't end up submitting a final entry which is too bad. this was an interesting one.
John
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Eugene Kirpichov Web IR developer, market.yandex.ru _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

I spent too much time reading the files, until today, when Minh Tuh pointed
me the right direction on reading the floats...
Anyway, I will still keep trying until Xmas :-)
On Mon, Jun 29, 2009 at 15:40, Justin Bailey
Anyone have thoughts to share? I'd love to read others' experiences but there isn't much coming up with searches or on redditt ...
I was happiest with the VM I implemented. Sadly, I wasn't able to solve any of the scenarios, but my VM ran damn fast. That didn't seem to matter so much this year as it did two years ago - I think I am still scarred by Endo's DNA ..
Anyways, for those who care, the heart of my VM implementation was a monadic fold over the program, with a mutable array representing the machine's memory, all inside ''runSTUArray.'' I used a simple data type to represent the machine:
data Machine = Machine { instrs :: [OpCode] , dataMem :: UArray Int Val , outputs :: UArray Int Val , status :: !Int , inputs :: IntMap Val }
where ''dataMem'' holds the data memory area. To execute a program, I folded over the ''instrs'' list and updated memory as I went:
exec :: Machine -> Input -> Machine exec m@(Machine { instrs = instrs }) inps = let m' = m { inputs = readInputs inps } newMem = runSTUArray $ do mem' <- thaw (dataMem m) foldM (step m') mem' (zip instrs [0..])
I considered ''unsafeThaw'' in the above, but performance didn't hurt me much and it seemed safer. Since I was recording all output I was worried that would break referential integrity. In any case, when the fold was finished I just assigned ''newMem'' to the new machine and moved to the next iteration.
''step'' takes the mutable array and updates it while the program executes. I won't show the whole body, as it just a separate definition for each opcode, just the type:
step :: Machine -> STUArray s Int Val -> (OpCode, Addr) -> ST s (STUArray s Int Val)
The only downside to this approach was the machine had three mutuable states: memory, output, and status. runSTUArray only wants to return a single array so inside step I had to hold all values in one array. Not a big deal but a little ugly.
I kept a diary of my experience over the weekend at http://icfp2009.codeslower.com. I started out with such optimism ... Stil, I'm glad to have done it. Lots of fun! My thanks to the organizers! _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Rafael Gustavo da Cunha Pereira Pinto Electronic Engineer, MSc.

On Mon, Jun 29, 2009 at 11:40:28AM -0700, Justin Bailey wrote:
Anyways, for those who care, the heart of my VM implementation was a monadic fold over the program, with a mutable array representing the machine's memory, all inside ''runSTUArray.'' I used a simple data type to represent the machine:
data Machine = Machine { instrs :: [OpCode] , dataMem :: UArray Int Val , outputs :: UArray Int Val , status :: !Int , inputs :: IntMap Val }
where ''dataMem'' holds the data memory area. To execute a program, I folded over the ''instrs'' list and updated memory as I went:
Nice. I did something pretty similar, although I just used association lists for the inputs and outputs---although I bet it would have been faster if I used arrays like you did! I plan to post some thoughts on my blog (syndicated on Planet Haskell) sometime later today or tomorrow. -Brent

Similar to mine except that I implemented with all of the memory (data, instruction, input and output ports) with the Data.Map library. One thing to care about is the heap memory profiling. You'll need to make sure that Map.insert function do not pile up as thunk. This is a typical memory leak profiling with most purely functional lazy data structures. I only tried the first problem, and it was good enough performance. But I don't have an idea how may timesteps it takes for the Operations Clear Sky.
participants (8)
-
Ahn, Ki Yung
-
Brent Yorgey
-
Edward Kmett
-
Eugene Kirpichov
-
John Meacham
-
Justin Bailey
-
Miguel Mitrofanov
-
Rafael Gustavo da Cunha Pereira Pinto