induceBackwardãé«éåãããã¨ãã
Haskell vs F# - Life Goes Onãæ°ã«ãªã£ãã®ã§ãã£ã¦ã¿ãã
æå ã«F#ã®å®è¡ç°å¢ããªãã®ã§ãå ã®ã³ã¼ãã2åé«éåãããã¨ãç®æ¨ã«ãã¦ã¿ããç°å¢ã¯Linux x64, GHC 7.0.4.
æåã®ã³ã¼ãã
import Data.Array.Unboxed data Node = Node { df :: Double, branch :: [(Int, Double)] } induceBackward :: Array Int Node -> UArray Int Double -> UArray Int Double induceBackward nodes values = accumArray (+) 0 (bounds nodes) [(j, p * values ! k * df) | (j, Node df branch) <- assocs nodes, (k, p) <- branch] iteration = 1000 main :: IO() main = print (maximum [value i | i <- [1..iteration]]) where value i = foldr induceBackward (lastValues i) testTree ! 0 lastValues i = listArray (-100, 100) (repeat (fromIntegral i)) testTree = [listArray (-i, i) [Node 1.0 [(j-1, 1.0/6.0), (j, 2.0/3.0), (j+1, 1.0/6.0)] | j <- [-i..i]] | i <- [0..99]]
å®è¡æéã
% time ./backward 999.9999999999998 ./backward 1.12s user 0.01s system 99% cpu 1.126 total
ã¾ãæ°ãä»ããã®ã¯ãä¸çªå å´ã®ã«ã¼ã(induceBackwardå ã®ã(k, p) <- branchãé¨å)ã§ãªã¹ãã辿ã£ã¦ãããã¨ããããé åä¸ã®foldã«ãã¦ãã¾ãããããã®ããã«ã¯ããä¸æ®µå¤ã®ã«ã¼ã(assocs nodeã®é¨å)ãé åä¸ã®mapã«ãããããããããã¯ArrayããUArrayã¸ã®å¤æãªã®ã§å¹ççã«ããã®ã¯é¢åãããã§arrayãããã¦vectorããã±ã¼ã¸ã使ããã¨ã«ããã
vectorããã±ã¼ã¸ã使ãã¨ãvãboxed vectorãªã次ã®ããã«ãã¦unboxed vectorã«å¤æãã¤ã¤mapã§ããã
-- import qualified Data.Vector as V
V.convert (V.map f v)
V.convertã¯boxed vectorãunboxed vectorã«å¤æããé¢æ°ã ããèåå¤æã®éæ³ã«ãã£ã¦V.mapã§ä½ããã¦V.convertã§æ¶è²»ãããä¸éã®boxed vectorãæé¤ãããã®ã§ãå ¨ä½ã¨ãã¦mapä¸ååã®ã³ã¹ãã§æ¸ãã
vectorããã±ã¼ã¸ã®vectorã¯arrayããã±ã¼ã¸ã®é åã¨ã¡ãã£ã¦æ·»åã®ãªãã»ãããæå®ã§ããªãã®ã§ããããåããã¦åArrã¨UArrãä½ããã¨ã«ããã
import qualified Data.Vector as V import qualified Data.Vector.Unboxed as U data Node = Node { df :: Double, branch :: [(Int, Double)] } type Arr a = (Int, V.Vector a) type UArr a = (Int, U.Vector a) (!) :: (U.Unbox a) => UArr a -> Int -> a (offset, vec) ! k = vec U.! (k - offset) induceBackward :: Arr Node -> UArr Double -> UArr Double induceBackward (nodesOffset, nodes) values = (nodesOffset, newValues) where newValues = V.convert $ V.map f nodes f (Node df branch) = sum [p * values ! k * df | (k, p) <- branch] iteration = 1000 main :: IO() main = print (maximum [value i | i <- [1..iteration]]) where value i = foldr induceBackward (lastValues i) testTree ! 0 lastValues i = (-100, U.replicate 201 (fromIntegral i)) testTree = [(-i, V.fromList [Node 1.0 [(j-1, 1.0/6.0), (j, 2.0/3.0), (j+1, 1.0/6.0)] | j <- [-i..i]]) | i <- [0..99]]
./backward2 3.01s user 0.02s system 99% cpu 3.034 total
ããªãé ããªã£ãããæ°ã«ããªãã§æ¬¡ã«ãããbranchãvectoråã«å¤æ´ã
data Node = Node { df :: Double, branch :: U.Vector (Int, Double) }
f (Node df branch) = U.sum $ U.map (\(k, p) -> p * values ! k * df) branch
ãã®(U.sum $ U.map ...)ããã¯ãèåå¤æã§ä¸évectorã®ãªãå½¢ã«ãªã£ã¦ãããã¨ãæå¾ ãã¦ããã
./backward3 1.15s user 0.01s system 99% cpu 1.163 total
å®è¡æéã¯æåã®ã³ã¼ãã¨ã»ã¼åãã«æ»ã£ãã
induceBackwardã®å¼æ°valuesãå å´ã®ã«ã¼ãã§é »ç¹ã«ä½¿ããã¦ããã®ã§æ£æ ¼ã«ããã
induceBackward (nodesOffset, nodes) values@(!_, !_) = (nodesOffset, newValues)
./backward4 0.76s user 0.01s system 99% cpu 0.766 total
ãã®æç¹ã§ã³ã¢ãèªãã§ã¿ããæããªç¡é§ãè¦ã¤ãããªãã£ããNodeã®dfãã£ã¼ã«ããæ£æ ¼ã«ãã¦UNPACKãã©ã°ããä»ãã¦ã¿ãã
./backward6 0.73s user 0.01s system 99% cpu 0.743 total
åæ§ã«branchãã£ã¼ã«ãã«ãUNPACKæå®ãããã¨ããã ãããªããåæãUNPACKãããã¨ã¯ã§ããªãã
LLVMããã¯ã¨ã³ããéããããªã®ã§è©¦ãã¦ã¿ããæå ã®LLVMãæ°ãããã(3.0)ããGHC 7.0ã§ã¯å¯¾å¿ãã¦ããªãã®ã§GHC 7.4.1ãã¤ã³ã¹ãã¼ã«ããã
./backward6-7.4.1 0.74s user 0.01s system 99% cpu 0.751 total ./backward6-7.4.1-llvm 0.71s user 0.01s system 99% cpu 0.724 total
ãã®ããããéçãã¨æã£ãããbranchã®é·ãã3åºå®ã§ãããã¨ãå©ç¨ãã¦ã«ã¼ããã¢ã³ãã¼ã«ããããã«äºç®æãã(U.!)ãU.unsafeIndexã«ç½®ãæãããã¨ã§åçãªé«éåãã§ãããã¨ã«å¶ç¶æ°ã¥ããã
./backward7-7.4.1-llvm 0.39s user 0.00s system 99% cpu 0.401 total
ã¾ã¨ã
2åãè¶ããé«éåã¯éæãããéç¶ã¨ããªãããªãã¢ã³ãã¼ã«ã«ããã»ã©ã®å¹æãããã®ãåãããªãããunsafeIndexãå¿ é ãªã®ãæ°åãæªãããã¨ã¯ãã£ã¨è©³ãã人ã«ä»»ãããã
ãã¨F#éãã§ããã
æçµçãªã³ã¼ãã
{-# LANGUAGE BangPatterns #-} import qualified Data.Vector as V import qualified Data.Vector.Unboxed as U data Node = Node { df :: {-# UNPACK #-} !Double, branch :: U.Vector (Int, Double) } type Arr a = (Int, V.Vector a) type UArr a = (Int, U.Vector a) (!) :: (U.Unbox a) => UArr a -> Int -> a (!) (offset, vec) k = vec `U.unsafeIndex` (k - offset) induceBackward :: Arr Node -> UArr Double -> UArr Double induceBackward (nodesOffset, nodes) values@(!_, !_) = (nodesOffset, newValues) where newValues = V.convert $ V.map f nodes f (Node df branch) = fold3 0 $ \i s -> case branch `U.unsafeIndex` i of (k, p) -> p * values ! k * df + s fold3 :: a -> (Int -> a -> a) -> a fold3 x f = f 0 $ f 1 $ f 2 x iteration = 1000 main :: IO() main = print (maximum [value i | i <- [1..iteration]]) where value i = foldr induceBackward (lastValues i) testTree ! 0 lastValues i = (-100, U.replicate 201 (fromIntegral i)) testTree = [(-i, V.fromList [Node 1.0 $ U.fromList [(j-1, 1.0/6.0), (j, 2.0/3.0), (j+1, 1.0/6.0)] | j <- [-i..i]]) | i <- [0..99]]
ãã¾ã
ä¿å®æ§ã¨ãå®å ¨æ§ã¨ãç¡è¦ãã¦éãã追æ±ããããããªã£ãã
./backward-fast 0.20s user 0.00s system 99% cpu 0.205 total
ããã¾ã§ãã¦ãã£ã¨ãé©å½ã«æ¸ããC++ã³ã¼ãã¨åçã
./a.out 0.21s user 0.00s system 99% cpu 0.214 total
ã³ã¼ãã¯ä»¥ä¸ã
{-# LANGUAGE BangPatterns #-} import Control.Monad import Control.Monad.ST import qualified Data.Primitive as P import qualified Data.Vector as V import qualified Data.Vector.Unboxed as U data Node = Node { df :: {-# UNPACK #-} !Double, branchIndex :: {-# UNPACK #-} !P.ByteArray{- Int -}, branchCoefficient :: {-# UNPACK #-} !P.ByteArray{- Double -} } type Arr a = (Int, V.Vector a) type UArr a = (Int, U.Vector a) (!) :: (U.Unbox a) => UArr a -> Int -> a (!) (offset, vec) k = vec `U.unsafeIndex` (k - offset) induceBackward :: Arr Node -> UArr Double -> UArr Double induceBackward (nodesOffset, nodes) values@(!_, !_) = (nodesOffset, newValues) where newValues = V.convert $ V.map f nodes f (Node df branchIndex branchCoefficient) = fold3 0 $ \i s -> s + P.indexByteArray branchCoefficient i * values ! P.indexByteArray branchIndex i * df fold3 :: a -> (Int -> a -> a) -> a fold3 x f = f 0 $ f 1 $ f 2 x iteration = 1000 main :: IO() main = print (maximum [value i | i <- [1..iteration]]) where value i = foldr induceBackward (lastValues i) testTree ! 0 lastValues i = (-100, U.replicate 201 (fromIntegral i)) testTree = [(-i, V.fromList [Node 1.0 (byteArrayFromList [j-1, j, j+1]) coefficients | j <- [-i..i]]) | i <- [0..99]] coefficients = byteArrayFromList [1.0/6.0, 2.0/3.0, 1.0/6.0::Double] byteArrayFromList :: (P.Prim a) => [a] -> P.ByteArray byteArrayFromList xs = runST $ do mut <- P.newByteArray (length xs * P.sizeOf (head xs)) forM_ (zip [0..] xs) $ \(i, v) -> P.writeByteArray mut i v P.unsafeFreezeByteArray mut
# include <vector> # include <utility> # include <memory> # include <cstdio> using namespace std; struct node { double df; vector<pair<int, double> > branch; }; auto_ptr<vector<double> > induce_backward(const vector<node> &nodes, const vector<double> &values) { const int n_nodes = nodes.size(); auto_ptr<vector<double> > ret(new vector<double>()); ret->reserve(n_nodes); const int n = values.size() / 2; for(int j = 0; j < n_nodes; j++) { double sum = 0; const node &node = nodes[j]; for(int k = 0; k < 3; k++) sum += node.branch[k].second * values[n + node.branch[k].first] * node.df; ret->push_back(sum); } return ret; } const int iteration = 1000; int main() { vector<vector<node> > test_tree; for(int i = 0; i < 100; i++) { test_tree.push_back(vector<node>()); vector<node> &nodes = test_tree.back(); for(int j = -i; j <= i; j++) { nodes.push_back(node()); node &node = nodes.back(); node.df = 1.0; node.branch.push_back(make_pair(j-1, 1.0/6.0)); node.branch.push_back(make_pair(j, 2.0/3.0)); node.branch.push_back(make_pair(j+1, 1.0/6.0)); } } double maxval = 0; for(int i = 1; i <= iteration; i++) { auto_ptr<vector<double> > values(new vector<double>(201, (double)i)); for(int j = 99; j >= 0; j--) values = induce_backward(test_tree[j], *values); maxval = max(maxval, (*values)[0]); } printf("%f\n", maxval); }