是否可以在D中的运行时创建一个连续的多维数组



我想在运行时创建一个矩形多维数组,这样整个数组就存储在一个连续的内存块中。数组在初始创建后不需要调整大小。实际上,我想在运行时创建一个静态数组,但我会接受一种满足所述条件的方法,即使数组在技术上是不同的类型。

更正式地说,我想取两个ulong,即nrnc,并在运行时创建一个数组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中删除了边界检查

运行时

该表报告了五次运行的平均运行时间(以毫秒为单位(。

>ldc-O320978+/-43415452+/-117820778+/-101720782+/-50113476+/-20920632+/-156
dmd-Ogdc-O3gdc-Os
基线13592+/-1758
baseline_dynamic13481+/-339
组块15800+/-14421014+/-17613676+/-11821203+/-212
chunksarray12903+/-64912895+/-51919770+/-816
阵列2d12606+-60619844+/-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

最新更新