我需要乘以[2 ^ 14 x 2 ^ 14]个矩阵。但首先,让我们谈谈[2 ^ 12 x 2 ^ 12]矩阵。我需要多少内存?假设它是双矩阵,所以我需要8个字节用于一个元素。
((2 ^ 12 * 2 ^ 12 * 8)/ 2 ^ 20)* 3 = 384 MiB
这是最糟糕的情况,当我需要将所有三个矩阵存储在内存中时。
haskell需要多少内存?我们来看看。
-> let m n = matrix (2^n) (2^n) ( \(i, j) -> (fromIntegral i) * (fromIntegral j) ) :: Matrix Double
-> let p n = let r = m n in multStd2 r r ! (n,n)
-> p 12
3.299742941184e12
(4.84 secs, 1,991,404,304 bytes)
这是~2 GiB。为什么这么糟糕,除了使用C ++我该怎么办?
UPD:
我正在使用标准的haskell的Data.Matrix模块。
答案 0 :(得分:5)
两点。
您正在寻找“未装箱”的数组(或矢量或矩阵或其他),而非“盒装”数组。未装箱的阵列具有与C相同的平面表示,而装箱阵列是指向存储在阵列外部的堆对象的指针数组。作为额外空间使用的交换,盒装数组可以存储任何类型的元素,还可以存储未评估的值,使其内容不严格。对于与机器字相同大小的类型(假设为64位系统),如Double
,盒装数组通常使用的空间是未装箱数组的三倍。
数字“1,991,404,304字节”并不代表您认为它意味着什么。它实际上是在计算期间分配的总内存量。这并没有说明任何时候使用的最大空间量(除了它小于2GB)。对于峰值空间使用值,请使用+RTS -s
运行,并在程序完成时查看输出中的“最大驻留时间”值。
答案 1 :(得分:3)
为什么这么糟糕,除了使用C ++,我该怎么做。
确保您使用-O2
。在我的测试中,它减少了最大内存
居住地从1095M到544M。运行时间也从3.48秒减少到2.65秒。
尝试使用更高效的库,如评论中所述。尝试
例如hmatrix
。
现在,至于“为什么”。以下是Matrix
中matrix
类型的实现方式
库:
data Matrix a = M {
nrows :: {-# UNPACK #-} !Int -- ^ Number of rows.
, ncols :: {-# UNPACK #-} !Int -- ^ Number of columns.
, rowOffset :: {-# UNPACK #-} !Int
, colOffset :: {-# UNPACK #-} !Int
, vcols :: {-# UNPACK #-} !Int -- ^ Number of columns of the matrix without offset
, mvect :: V.Vector a -- ^ Content of the matrix as a plain vector.
}
(假设我们使用的是64位系统)
我们有5个解压缩的整数,这使得5 x 8 bytes = 40 bytes
。为了
实际的矢量数据我们有一个Vector
。这不是unboxed
vector,
这意味着向量的每个元素都是指向分配的堆的指针
宾语。在我们的例子中,我们有这些功能:
m :: Int -> Matrix Double
m n =
matrix (2^n) (2^n) ( \(i, j) -> (fromIntegral i) * (fromIntegral j) )
p :: Int -> Double
p n = let r = m n in multStd2 r r ! (n,n)
(添加类型以使其清楚)
因此我们的Matrix Double
大小为2^12 * 2^12 = 16777216
双倍。
现在估计将多少内存存储在内存中 堆,让我们看一下GHC的堆对象表示。
堆对象表示为:
typedef struct StgClosure_ {
StgHeader header;
struct StgClosure_ *payload[FLEXIBLE_ARRAY];
} *StgClosurePtr;
typedef struct {
const StgInfoTable* info;
} StgHeader;
typedef struct StgInfoTable_ {
StgClosureInfo layout; /* closure layout info (one word) */
StgHalfWord type; /* closure type */
StgHalfWord srt_bitmap;
StgCode code[FLEXIBLE_ARRAY];
} *StgInfoTablePtr;
typedef union {
struct { /* Heap closure payload layout: */
StgHalfWord ptrs; /* number of pointers */
StgHalfWord nptrs; /* number of non-pointers */
} payload;
StgWord bitmap; /* word-sized bit pattern describing */
/* a stack frame: see below */
OFFSET_FIELD(large_bitmap_offset); /* offset from info table to large bitmap structure */
StgWord selector_offset; /* used in THUNK_SELECTORs */
} StgClosureInfo;
我们这里有很多簿记。为了简单起见,我们假设
matrix
库足够有效,可以避免在这里发生thunking(即matrix
函数是严格的返回值)。在这种情况下,我们可以说我们的
payload
只会有实际的双倍。所以这是数学:
struct StgClosure_ *
表示Double type = 16777216 * 8字节。
- 我们的有效载荷只有双打= 16777216 * 8字节。
StgInfoTable*
= 16777216 * 8字节。StgInfoTable
包括:
StgClosureInfo
=至少3个字= 24个字节。当我们将它们相加时,它会产生1073M。
现在,这场比赛非常波动,但希望每一堆都清楚 对象那里有很多记账。更确切地说,我们需要 采取像共享信息表这样的东西(例如,据我所知,相同的闭包会 我认为有相同的信息表?)。