3 ms·
I like the idea. And at least this is a post that - says from the start that the solutions aren't optimal - shares the code that gives the quoted results righ
by darklajid 15y ago
I like the idea. And at least this is a post that
- says from the start that the solutions aren't optimal
- shares the code that gives the quoted results right away
- makes it easy to verify/check/contribute
First thing I notice though is that it's (looking at the functional F# code) using exceptions for control flow. It seems the author uses a 'NoMoreWork' exception as a kind of break out of a loop.
While my F# is rusty, I doubt that this is a good idea, probably neither beautiful nor fast..
Edit: Another couple of comments. Things that weren't immediately obvious to me after reading the blog entry alone:
- The make benchmark passes really just one board with two moves to an executable. Just like the time commands before. So what we're measuring is the startup time of the process (native vs. managed/clr) and the cost to find a single/first move.
- The whole engine thing is designed around the concept of 'I pass the whole board as arguments'. So the driver seems to compute a string representation of the board after each move and _create a new process of the engine_. So - yes, this is a bad idea for managed code. Or anything that could otherwise use JIT.
- ttsiodras 15y agoThank you for your kind words. To your points: - You are right about measuring the startup time of the binaries with "time"; but in this case, where the execution time for F# is measured in the order of ten seconds, the comparisons are still valid and useful, especially in the context of seeing what kind of an impact switching to imperative-style has (10->8, 1.7->1.4, etc) - Passing the whole board as arguments has little (if any) effect on the execution time: e.g. you can see for yourself that if you pass NO arguments (i.e. clean board) the time ratios between languages remain the same. In a game like Score4, the human has to think anyway - and you can see that using C++, even the nasty cmd-line interface leads to response times of less than a second. - About F# exceptions: in the absence of "break"... can I do anything else to abort a loop early? Thanks for your feedback, much appreciated.
- pbz 15y agoIt may not speed up F# enough to reach OCaml, but it may be enough to bring C# within range.
- deleted 15y ago[deleted]
- zmj 15y agoDon't loop and abort - use recursion.
- Dn_Ab 15y agoYou could use a while loop or recursion instead of a for loop. A couple of simple things I noticed is that the complexity of your functional code is higher than the imperative - it shows the speed of ocaml that it was able to get you so much performance. Although, one way to give some advantage back to F# would be to parallelize your code. Last I checked F# allows you to do this more easily due to constructs like async and agents and .net parallel stuff and in a better way since OCaml has a GIL. ------------ List.map (abMinimax (not maximizeOrMinimize) (otherColor color) (depth-1)) |> List.map snd iterates through the list twice. You could just as easily have removed the second List.map. You can replace (map |> filter) with a fold. allData |> List.sortBy getScore |> List.rev |> List.head could sort by negative score. There are a couple other suggestions I could give to replace the use of reference cells and for loops with recursion, comprehensions, unfolds or folds. Some would not be speed improvements but would yield shorter more colloquial code. But I unfortunately can't afford to donate that time at the moment. Sorry I could not give more concrete advice.
- ttsiodras 15y agoI replaced the exceptions from the functional F# code with mutable flags and while loops - and its speed improved from being 6 times slower than OCaml, to being 5 times slower. I also replaced the sort with a fold... and there was no speed improvement (the lists are so small it made no difference). Oh well, what can you do? :-)