2016-08-10 2 views
5

하스켈에서 큰 정수 행렬을 정렬해야하고 임의의 데이터로 벤치마킹을 시작했습니다. 나는 하스켈이 C++보다 3 배 느리다는 것을 발견했다.하스켈 : 벡터 정렬보다 훨씬 느린 행렬 정렬

임의성 때문에 줄 비교가 항상 첫 번째 열 (중복이 없어야 함)에서 끝나기를 기대합니다. 그래서 행렬을 Vector (Unboxed.Vector Int)로 구현 된 단일 열로 축소하고 일반적인 Vector Int와 비교했습니다.

벡터 Int는 C++만큼 빨리 정렬하지만 좋은 결과입니다. 그러나 다시 열 매트릭스는 3 배 느립니다. 왜 그런 생각이 드나요? 아래 코드를 찾으십시오. 들어

import Control.Monad.Primitive 
import Data.Primitive.ByteArray 
import qualified Data.Vector.Generic.Mutable.Base as GM(MVector(..)) 
import GHC.Prim 

data MutableArrayArray s a = MutableArrayArray (MutableArrayArray# s) 

instance GM.MVector MutableArrayArray ByteArray where 
    {-# INLINE basicLength #-} 
    basicLength (MutableArrayArray marr) = I# (sizeofMutableArrayArray# marr) 

    {-# INLINE basicUnsafeRead #-} 
    basicUnsafeRead (MutableArrayArray marr) (I# i) = primitive $ \s -> case readByteArrayArray# marr i s of 
    (# s1, bar #) -> (# s1, ByteArray bar #) 

    {-# INLINE basicUnsafeWrite #-} 
    basicUnsafeWrite (MutableArrayArray marr) (I# i) (ByteArray bar) = primitive $ \s -> 
    (# writeByteArrayArray# marr i bar s,() #) 

: ArrayArray#로 벡터의 벡터를 구현 dfeuer의 조언에 따라

import qualified Data.Vector.Unboxed as UV(Vector, fromList) 
import qualified Data.Vector as V(Vector, fromList, modify) 
import Criterion.Main(env, bench, nf, defaultMain) 
import System.Random(randomIO) 
import qualified Data.Vector.Algorithms.Intro as Alg(sort) 

randomVector :: Int -> IO (V.Vector Int) 
randomVector count = V.fromList <$> mapM (\_ -> randomIO) [1..count] 

randomVVector :: Int -> IO (V.Vector (UV.Vector Int)) 
randomVVector count = V.fromList <$> mapM (\_ -> do 
               x <- randomIO 
               return $ UV.fromList [x]) [1..count] 

benchSort :: IO() 
benchSort = do 
    let bVVect = env (randomVVector 300000) $ bench "sortVVector" . nf (V.modify Alg.sort) 
     bVect = env (randomVector 300000) $ bench "sortVector" . nf (V.modify Alg.sort) 
    defaultMain [bVect, bVVect] 

main = benchSort 
+0

권투 일 수도 있습니다. C++에서 다차원 배열보다는 개별적으로 할당 된 행에 대한 포인터의 배열로 사용해보십시오 (필자는 여기 있습니다). 나는 다차원 벡터가 지원된다고 생각하지 않습니다, 그래서 이것이 진행된다면 행렬을 크기 n * m의 벡터로 표현하기 위해 약간의 추상화 작업을해야 할 것입니다. – luqui

+0

@luqui를 기반으로 작성된 C++ 다차원 배열은 메모리에서 하나의 연속 블록이지만 여기에는 unboxed 벡터에 대한 참조 벡터가 있습니다. ['array'] (https://hackage.haskell.org/package/array) 또는 ['repa'] (https://hackage.haskell.org/package)를 사용하면 성능이 상당히 향상 될 것으로 기대합니다./repa). – Alec

+1

나는 std :: vector >을 C++로 비교 했으므로 Haskell의 Vector (Vector Int)와 동일하다. 즉 벡터에 대한 포인터 벡터이다. 내 Matrix를 크기가 n * m 인 Vector Int로 패킹하려고 생각했지만 그때 Ints 블록을 한 번에 바꿀 수있는 정렬이 없습니다. 그리고 블록 스왑을 사용했다하더라도 벡터에 대한 포인터를 정렬하는 것보다 효율적이지는 않습니다 (메모리에 너무 많은 쓰기가 있음). –

답변

1

를 사용 정렬 하스켈 버전 간접적 인 여분의 층을 갖는다. UV.Vector

data Vector a = Vector !Int !Int ByteArray# 

같은 그래서 벡터하여 벡터의 각 항목에 실제로 기록 유지 슬라이스 인덱스에 대한 포인터와 바이트의 배열에 대한 포인터입니다 보인다. 이것은 C++ 코드가 가지고 있지 않은 여분의 간접 참조입니다. 해결 방법은 ArrayArray#을 사용하는 것입니다.이 배열은 바이트 배열에 대한 직접 포인터 배열이거나 ArrayArray#입니다. vector가 필요하면 조각 기계에 대해해야 할 일을 파악해야합니다. 또 다른 옵션은 더 간단한 배열을 제공하는 primitive으로 전환하는 것입니다.

1

는 C++ std::vector<std::vector<int> > 정렬에 비해 4 배 벡터 (Unboxed.Vector INT)보다 빠른 만 40 % 느린 예를 들어, 저 바와 같이 정수 행렬은 다음 에드워드 Kmett 같이

sortIntArrays :: ByteArray -> ByteArray -> Ordering 
sortIntArrays x y = let h1 = indexByteArray x 0 :: Int 
         h2 = indexByteArray y 0 :: Int in 
        compare h1 h2 
관련 문제