-- Author: Ryan Newton -- In this version we flatten our (x,y) positions into a single integer. -- In this improved version, we look at three different optimizations. -- First: enable GMaps and pack -- Second: use cncFor to structure the spewing of the inital workload. -- Third: do the work *inside* a cncFor and thereby drastically reduce the step count. -- Set the environment variable MANDELOPT to {1,2,3} to add each of these optimizations. import Data.Complex import Data.Word import System.Environment import Control.Monad import Data.Bits -- #define MEMOIZE #define USE_GMAP #include -- Here we manually pack our pairs into scalars. -- In the future the ItemCol data type may do this for us auto-magically. type Pair = (Word16, Word16) pack :: Pair -> Int unpack :: Int -> Pair pack (a,b) = shiftL (fromIntegral a) 16 + (fromIntegral b) unpack n = (fromIntegral$ shiftR n 16, fromIntegral$ n .&. 0xFFFF) mandel :: Int -> Complex Double -> Int mandel max_depth c = loop 0 0 where fn = magnitude loop i z | i == max_depth = i | fn(z) >= 2.0 = i | otherwise = loop (i+1) (z*z + c) dynAPI = True mandelProg :: Int -> Int -> Int -> Int -> GraphCode Int mandelProg optlvl max_row max_col max_depth = do position :: TagCol Int <- newTagCol dat :: ItemCol Int (Complex Double) <- newItemCol pixel :: ItemCol Int Int <- newItemCol let mandelStep tag = do cplx <- get dat tag put pixel tag (mandel max_depth cplx) prescribe position mandelStep let packit i j = (pack (_i,_j), z) where (_i,_j) = (fromIntegral i, fromIntegral j) z = (r_scale * (fromIntegral j) + r_origin) :+ (c_scale * (fromIntegral i) + c_origin) let kernel i j = do let (packed,z) = packit i j put dat packed z if dynAPI then forkStep$ mandelStep packed else putt position packed let init1 = forM_ [0..max_row] $ \i -> forM_ [0..max_col] $ \j -> kernel i j -- This version uses cncFor to structure the work spawning. let init2 = do stepPutStr "mandel_opt: Using cncFor implementation...\n" cncFor2D (0,0) (max_row, max_col) $ \ i j -> kernel i j -- This version uses cncFor to do the actual work. let init3 = do stepPutStr "mandel_opt: Using even better cncFor method...\n" cncFor2D (0,0) (max_row, max_col) $ \ i j -> do let (packed,z) = packit i j put pixel packed (mandel max_depth z) initialize $ case optlvl of 1 -> init1 2 -> init2 3 -> init3 -- Final result, count coordinates of the pixels with a certain value: finalize $ foldM (\acc i -> foldM (\acc j -> do p <- get pixel (pack (fromIntegral i, fromIntegral j)) if p == max_depth then return (acc + (i*max_col + j)) else return acc) acc [0..max_col] ) 0 [0..max_row] where r_origin = -2 :: Double r_scale = 4.0 / (fromIntegral max_row) :: Double c_origin = -2.0 :: Double c_scale = 4.0 / (fromIntegral max_col) :: Double runMandel optlvl a b c = do putStrLn$ "Running mandel with opt level: "++ show optlvl let check = runGraph $ mandelProg optlvl a b c putStrLn ("Mandel check " ++ show check) main = do args <- getArgs case args of [] -> runMandel 1 3 3 3 -- Should output 24. [o, a,b,c] -> runMandel (read o) (read a) (read b) (read c)