3

I am learning clojure, and implementing my standard test project, Tic Tac Toe 10. I have written the same AI in many languages before and have had problems scaling it beyond 6 moves ahead in the other languages also.

I got the AI algorithm basically working, but I'm trying to improve the speed with pmap. Since everything is immutable, I should be able to just drop in pmap in place of map and get the same results, but I'm not seeing that.

(defn get-spot
  [board player win-cond levels]
  (def avail (get-available board))
  (def final
    (apply merge
           (map #(array-map % (calc-score board player win-cond levels %)) avail)))
  final)

But pmap in that spot returns inconsistent results. Not sure where to start looking. I can post more of the code if it's needed.

Vaibhav Mule
  • 5,016
  • 4
  • 35
  • 52
  • 8
    Don't use `def` inside functions, because `def` always creates bindings at namespace scope, never to the scope of your function. If `calc-score` uses `def` for values internal to its calculation, `pmap` will give erroneous results. Use `let` to create bindings that need to be local to one function. – noisesmith Apr 20 '15 at 03:59
  • Brian - What have you isolated as the 'non-performant' part of this? Do 'get-available' or 'calc-score' do anything of comp time import? You mentioned pmap but you are showing array-map. Do you have any comparative timings? – Frank C. Apr 20 '15 at 10:09
  • Yeah, definitely using def everywhere. Let me try and replace function local vars with let and see if that fixes my parallelism problems. – Brian Baritonehands Gregg Apr 20 '15 at 21:45

2 Answers2

1

Replacing def with let everywhere solved the problem. My functions had lots of inconsistent side effects if I didn't use let.

(defn get-spot
  [board player win-cond levels]
  (let [avail (get-available board)
        final (apply merge
           (pmap #(array-map % (calc-score board player win-cond levels %)) avail))]
  final))

I've written a bunch of clojure code, and read through many tutorials. I don't know how I missed that really important detail.

-1

Please read a book or two about clojure before you even consider to do performance tuning. Having immutability built into a language does not mean you can just replace map by pmap.

Two more things to consider are the absence of side effects for your code and the fact that operations will have to be commutativ. If they are not, you will have to consider this in your merge algorithm.

As others already said, don't create def bindings insider functions, use let instead. Also, the last expression will be returned from a function anyway, so you don't need to bind final before returning it.

I am sorry for not giving a direct solution to your problem, I just think you need to understand that replacement of map is not as easy as it may seem.

If you still want your problem solved I guess we need to see more code here.

sveri
  • 1,372
  • 1
  • 13
  • 28