我想在运行时创建一个矩形多维数组,这样整个数组就存储在一个连续的内存块中。数组在初始创建后不需要调整大小。实际上,我想在运行时创建一个静态数组,但我会接受一种满足所述条件的方法,即使数组在技术上是不同的类型。
更正式地说,我想取两个ulong
,即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])
,则编译失败。
答案比较
为了比较提供的不同答案,我在10000000 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是BeardBeaver的方法
- chunks是Bearded Beaver在没有.array的情况下的方法
- array2D是Jack Applegame的(2D(方法,从opIndex中删除了边界检查
运行时
该表报告了五次运行的平均运行时间(以毫秒为单位(。
dmd-O | gdc-O3 | >gdc-Os | ldc-O3||
---|---|---|---|---|
基线 | 13592+/-1758 | 20978+/-43415452+/-117820778+/-1017|||
baseline_dynamic | 13481+/-339 | 20782+/-50113476+/-20920632+/-156|||
组块 | 15800+/-144 | 21014+/-176 | 13676+/-118 | 21203+/-212 |
chunksarray | 12903+/-649 | 12895+/-519 | 19770+/-816 | |
阵列2d | 12606+-606 | 19844+/-1042 | > | 19724+/-953[/td> |
D中的静态和动态数组在内存中的布局不同。
动态数组是引用类型,静态数组是值类型。
因此,没有办法将内存块投射到多维动态数组中。
相反,您将不得不使用重载的索引运算符创建自己的类型。
基本示例:
import std.stdio : writeln;
void main() {
auto arr = Array2D!int(3, 4);
arr[2, 3] = 23;
// arr[3, 3] = 33; // range violation
writeln(arr[2, 3]);
writeln(arr.data[0..12]);
}
struct Array2D(T) {
import core.memory : GC;
import std.exception : enforce;
import core.exception : RangeError;
size_t nr, nc;
T* data;
this(size_t nr, size_t nc) {
this.nr = nr;
this.nc = nc;
auto ds = nr * nc;
data = cast(T*) GC.malloc(T.sizeof * ds);
data[0..ds] = 0;
}
ref T opIndex(size_t r, size_t c) {
enforce!RangeError(r >= 0 && r < nr);
enforce!RangeError(c >= 0 && c < nc);
return data[c * nr + r];
}
}
基本通用示例:
import std.stdio : writeln;
void main() {
auto arr = makeArrayMD!int(2, 3, 4);
arr[1, 2, 3] = 123;
// arr[2, 2, 3] = 223; // range violation
writeln(arr[1, 2, 3]);
writeln(arr.data[0..24]);
}
struct ArrayMD(T, size_t dims) {
import core.memory : GC;
import std.algorithm : fold;
import std.exception : enforce;
import core.exception : RangeError;
size_t[dims] sizes;
T* data;
this(Sizes...)(Sizes szs) if(Sizes.length == dims) {
sizes = [szs];
auto ds = sizes.fold!"a * b";
data = cast(T*) GC.malloc(T.sizeof * ds);
data[0..ds] = 0;
}
ref T opIndex(Sizes...)(Sizes szs) {
size_t idx = 0;
size_t rs = 1;
static foreach(i, s; szs) {
enforce!RangeError(s >= 0 && s < sizes[i]);
static if(i > 0) rs *= sizes[i - 1];
idx += s * rs;
}
return data[idx];
}
}
auto makeArrayMD(T, DS...)(DS dims) {
return ArrayMD!(T, DS.length)(dims);
}
我使用chunks
创建了一个2D阵列,该阵列存储在一体式的内存中
auto p = new int[](nr * nc);
auto p1 = p.chunks(nc).array;
// now you can set and get values as in usual 2D array
p1[i][j] = 42;
更多信息请点击此处:https://dlang.org/library/std/range/chunks.html#1