0

I have array of x,y coordinates and char elements. I need function to get list of n chars at specified coordinates toward direction. I wrote it in recurrent way:

import Data.Array

exampleArray = array ((1,1),(2,2)) [((1,1),'a'), ((1,2),'b'), ((2,1),'c'), ((2,2),'d')]
--       1   2  
--     +---+---+
--   1 | a | b |
--     +---+---+
--   2 | c | d |
--     +---+---+

f :: Array (Int, Int) Char -> Int -> (Int, Int) -> (Int, Int) -> [Char]
f _ 0 _ _ = []
f arr n (x,y) (dirX, dirY) = (arr ! (y,x)) : f arr (n-1) (x+dirX,y+dirY) (dirX, dirY)

-- ac
list = f exampleArray 2 (1,1) (0,1) -- get 2 chars from array at 1,1 toward E

-- cb
list2 = f exampleArray 2 (1,2) (1,(-1)) -- get 2 chars from array at 1,2 toward NE

It works, but it is too slow and I am wondering how to optimize it?

andrzej1_1
  • 1,151
  • 3
  • 21
  • 38
  • You can redesign with primitives like this: `f arr n s (dx, dy) = take n . map (arr!) . iterate (\(!y, !x) -> (y + dy, x + dx)) . swap $ s`. And can you write your real simple example where your code work too slow? – freestyle Jun 16 '17 at 14:58
  • I get `Illegal bang-pattern (use BangPatterns): ! y` – andrzej1_1 Jun 16 '17 at 15:18
  • I cannot provide simple example, but consider searching patterns in thousands of arrays in gomoku AI. If you fix function I will provide time difference between both functions. – andrzej1_1 Jun 16 '17 at 15:26
  • Use extension `BangPatterns` to avoid compilation error. – freestyle Jun 16 '17 at 15:42
  • Getting best move was taking 17 seconds and with your function it takes 18. – andrzej1_1 Jun 17 '17 at 08:26
  • I don't have experience with Data.Array, maybe @Paul Johnson is right. But I have experience with [repa](https://www.stackage.org/lts-8.18/package/repa-3.4.1.2), it based on vector package, and I recomend it you – freestyle Jun 17 '17 at 11:31

1 Answers1

1

Array is slow, so your first step should be to port to the vector package. Use the unboxed variant, as the default "boxed" representation allocates heap storage for each value and stores a pointer to it. There is no such thing as a 2-d vector so you will have to write code to transform your coordinate pairs into locations in a 1-d vector. If that still isn't fast enough then access using the unsafeIndex function and its relatives, which don't do bounds checking (but do not call up that which you cannot put down).

See also this question.

Paul Johnson
  • 17,438
  • 3
  • 42
  • 59
  • I was looking for a way to optimize function, but if changing Array to unboxed vector help, I will accept your answer. Give me some time to do it. – andrzej1_1 Jun 17 '17 at 08:54
  • I can't see anything intrinsically slow about your function, other than its use of Array, and hence lots of unboxed values. One other point is that by going to a 1-d implementation all your moves (N, NE, E etc) can be represented by a single offset, so you only need to do a single addition per step. – Paul Johnson Jun 17 '17 at 11:15
  • Currently in array I am using own type like `data Square = Cross | Nought | Empty` so I can't just use vector, because it requires `Unbox` instance. Do you think It would be better to create / derive it or I should change all functions to work on chars? – andrzej1_1 Jun 17 '17 at 14:17
  • I managed to derive Unbox instance with `vector-th-unbox` and port program to Vector.Unboxed, but it runs two times slower: https://pastebin.com/7CEWNfFP – andrzej1_1 Jun 17 '17 at 19:54
  • Hard to say more without looking at the code. Are you compiling with optimisation on? – Paul Johnson Jun 18 '17 at 12:26
  • I am using only `-O2` option. If you tell me your bitbucket username I will give you access to private repository. – andrzej1_1 Jun 18 '17 at 13:19
  • I made repository public: https://bitbucket.org/andrzej1_1/gomoku/src/93ac3ed553c0869c63f768562369bbebee2fb55a?at=vector.unboxed Could you check the code? – andrzej1_1 Jun 18 '17 at 21:14
  • Having taken a quick look, I see that the movement through the board is still being done by tracking x and y separately. However in an nxm board held in a 1-d vector you can reduce the move in any direction to a single addition: a horizontal move is +/-1 and a vertical move is +/-m (and likewise for diagonal). You also have a lot of small functions, which is good style, but perhaps judicious use of the INLINE pragma might help. The profile you posted has a lot of Vector fusion binds in it, which suggests that the optimiser isn't able to fuse your operations into single loops for some reason. – Paul Johnson Jun 19 '17 at 17:05