我正在尝试从二进制文件中解析一个巨大的复杂值的 3d 数据数组。稍后这应该成为l
矩阵 ( n x m
)。由于我要处理这些矩阵,因此我仅限于矩阵库 - hmatrix 似乎很有希望。数据布局不是我要求的格式,所以我必须在 position 中跳来跳去(i,j,k) -> (k,i,j)
,其中i
和j
是 的元素n
和m
的k
元素l
。
我认为阅读这个的唯一方法是我使用可变变量,否则我最终会得到几个太字节的垃圾。我的想法是使用盒装互数组或互矩阵向量(来自 Numeric.LinearAlgebra.Devel 的 STMatrix),所以我最终得到如下结果:
data MVector s (STMatrix s t)
但我不确定如何正确使用它们:我可以通过 modify 修改 MVector 的一个元素:
modify :: PrimMonad m => MVector (PrimState m) a -> (a -> a) -> Int -> m ()
或使用 modifyM (奇怪:堆栈向量-0.12.3.0 中没有 modifyM...)
modifyM :: PrimMonad m => MVector (PrimState m) a -> (a -> m a) -> Int -> m ()
所以我可以使用函数调用(a -> a)
runST 例程来修改 SMatrix。我不确定,是否应该将 ST 放入 IO(?)
尽管如此 - 我认为,这应该有效,但只有在我想修改整个矩阵时才有用,调用这个(a->a)
-routine n x m x l
- 时间会有点开销(也许它会被优化出来......)。所以我最终会在编组数组时通过指针修改内容(i,j,k) -> (k,i,j)
并逐个矩阵读取所有内容 - 但这感觉不对,我想避免这种肮脏的技巧。
你有什么想法可以做到这一点,但更...干净?泰
编辑: 感谢 KA Buhr。他的解决方案到目前为止有效。现在,我只是遇到了一些性能影响。如果我比较解决方案:
{-# LANGUAGE BangPatterns #-}
module Main where
import Data.List
import Numeric.LinearAlgebra
import qualified Data.Vector as V
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Storable.Mutable as VSM
-- Create an l-length list of n x m hmatrix Matrices
toMatrices :: Int -> Int -> Int -> [C] -> [Matrix C]
toMatrices l n m dats = map (reshape m) $ VS.createT $ do
mats <- V.replicateM l $ VSM.unsafeNew (m*n)
sequence_ $ zipWith (\(i,j,k) x ->
VSM.unsafeWrite (mats V.! k) (loc i j) x) idxs (dats ++ repeat 0)
return $ V.toList mats
where idxs = (,,) <$> [0..n-1] <*> [0..m-1] <*> [0..l-1]
loc i j = i*m + j
test1 = toMatrices 1000 1000 100 (fromIntegral <$> [1..])
main = do
let !a = test1
print "done"
使用最简单的 C 代码:
#include <stdlib.h>
#include <stdio.h>
void main()
{
const int n = 1000;
const int m = 1000;
const int l = 100;
double *src = malloc(n*m*l * sizeof(double));
for (int i = 0; i < n*m*l; i++) {
src[i] = (double)i;
}
double *dest = malloc(n*m*l * sizeof(double));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
for (int k = 0; k < l; k++) {
dest[k*n*m+i*m+j] = src[i*m*l+j*l+k];
}
}
}
printf("done: %f\n", dest[n*m*l - 1]); // Need to access the array, otherwise it'll get lost by -O2
free(src);
free(dest);
}
使用 -O2 编译的两者都给出了以下性能猜测:
real 0m5,611s
user 0m14,845s
sys 0m2,759s
对比
real 0m0,441s
user 0m0,200s
sys 0m0,240s
这大约是每个核心性能的 2 个数量级。从分析中我了解到
VSM.unsafeWrite (mats V.! k) (loc i j) x
是昂贵的功能。由于我将在类似分钟的间隔中使用此过程,因此我希望将解析时间保持在与磁盘访问时间一样低的时间。我会看看,如果我能加快这个速度
PS:这是一些测试,如果我可以将通常的 DSP 从 C 类移动到 Haskell
Edit2: 好的,这是我尝试总和后得到的:
{-# LANGUAGE BangPatterns #-}
module Main where
import Data.List
import qualified Data.Vector as V
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Storable.Mutable as VSM
import Numeric.LinearAlgebra
-- Create an l-length list of n x m hmatrix Matrices
toMatrices :: Int -> Int -> Int -> VS.Vector C -> V.Vector (Matrix C)
toMatrices l n m dats =
V.map (reshape m) newMat
where
newMat = VS.createT $
V.generateM l $ \k -> do
curMat <- VSM.unsafeNew (m * n)
VS.mapM_
(\i ->
VS.mapM_
(\j -> VSM.unsafeWrite curMat (loc i j) (dats VS.! (oldLoc i j k)))
idjs)
idis
return curMat
loc i j = i * m + j
oldLoc i j k = i * m * l + j * l + k
!idis = VS.generate n (\a->a)
!idjs = VS.generate m (\a->a)
test1 = toMatrices 100 1000 1000 arr
where
arr = VS.generate (1000 * 1000 * 100) fromIntegral :: VS.Vector C
main = do
let !a = test1
print "done"
它给出了一些关于:
real 0m1,816s
user 0m1,636s
sys 0m1,120s
,所以比 C 代码慢 4 倍。我想我可以忍受这个。我想,我正在用这段代码破坏向量的所有流功能。如果有任何建议可以让它们以可比的速度返回,我将不胜感激!