【发布时间】:2022-01-01 05:34:02
【问题描述】:
我想在运行时创建一个矩形多维数组,以便整个数组存储在一个连续的内存块中。数组在初始创建后不需要调整大小。实际上,我想在运行时创建一个静态数组,但我会接受满足所述条件的方法,即使该数组在技术上属于不同类型。
更正式地说,我想取两个ulongs nr 和nc,并在运行时创建一个数组arr,这样arr[r][c] 就等于*(arr.ptr + r * nc + c),两者都在什么方面它评估,以及它这样做的效率。 (*(arr.ptr + c * nr + r) 也是可以接受的,虽然我不认为 D 会使用列优先顺序。)
有没有办法在 D 中做到这一点?
我得到的最接近的是:
import std.stdio, core.memory;
void main() {
ulong nr = 3, nc = 4;
auto p = cast(int*)GC.malloc(nr * nc * int.sizeof);
auto p1 = cast(int[3][4])p[0..12];
}
但如果我将 cast(int[3][4]) 更改为 cast(int[nr][nc]),则编译失败。
答案比较
为了比较提供的不同答案,我在 10,000,000 x 20 的双精度数组上运行了以下函数(根据需要调整了索引表达式)。
auto busywork(T)(T p) {
foreach (r; 0..nr) {
foreach (c; 0..nc) {
p[r][c] = r + log(1.0 + c);
}
}
double result = 1.0;
foreach (t; 0..1) {
foreach (r; 0..nr) {
foreach (c; 0..nc) {
result += log(1.0 + pow(p[r][c], 3));
}
}
}
return result;
}
- 基线是使用
p[r * nc + c]手动索引的一维数组。 - baseline_dynamic 是一个二维动态数组
- chunks_array 是 Bearded Beaver 的方法
- chunks 是 Bearded Beaver 不带 .array 的方法
- array2D 是 Jack Applegame 的 (2D) 方法,其中边界检查已从 opIndex 中移除。
运行时
该表报告了五次运行的中位运行时间(以毫秒为单位)。
| dmd -O | gdc -O3 | gdc -Os | ldc -O3 | |
|---|---|---|---|---|
| baseline | 13592 +/- 1758 | 20978 +/- 434 | 15452 +/- 1178 | 20778 +/- 1017 |
| baseline_dynamic | 13481 +/- 339 | 20782 +/- 501 | 13476 +/- 209 | 20632 +/- 156 |
| chunks | 15800 +/- 144 | 21014 +/- 176 | 13676 +/- 118 | 21203 +/- 212 |
| chunksarray | 12903 +/- 649 | 19648 +/- 1080 | 12895 +/- 519 | 19770 +/- 816 |
| array2d | 12606 +/- 606 | 19844 +/- 1042 | 12640 +/- 540 | 19724 +/- 953 |
内存使用
- chunks 和 array2d 的内存开销均为零。
- chunksarray 对所有编译器都有 9% 的开销。
- baseline_dynamic 在 dmd 下有 18% 的开销,在 gcc 和 ldc 下有 41% 开销。
自然,相对开销因数组尺寸和类型而异。
结论
-O3 和 -Os 之间的巨大差异,以及 array2d 在 dmd -O 下的事实在某些运行中比基线执行更好(尽管检查生成的程序集(一个较短的程序)显示opIndex 没有被 dmd 内联)表明缓存未命中的数量是主要因素,至少当阵列占用了相当比例的系统 RAM 时。 (设置 -boundscheck=off 可使 dmd -O 版本的基线与 array2d 性能保持一致,所以这显然是 array2d 有时更好的原因。)
chunksarray 的优点是代码更短并且使用与动态数组相同的语法,并且在 nc 和/或 T.sizeof 足够大以至于相对内存开销变得不那么重要的情况下可能会变得更可取,但是 array2d在一般情况下似乎是最佳选择。
【问题讨论】:
-
感谢对比表,干得好。我想知道为什么 ldc 这么慢,它被认为非常快
-
@BeardedBeaver 我怀疑不是 ldc 表现不佳,而是
-O3。gcc -O3的表现与ldc2 -O3相似,但gcc -Os表现相当不错。我在 ldc 手册中找不到优化代码大小的标志,因此表中没有。
标签: arrays d memory-layout