【问题标题】:Chapel: Array of arrays vs 2-dim array on memory教堂:数组数组与内存上的 2-dim 数组
【发布时间】:2017-07-13 20:58:40
【问题描述】:

在 Chapel 中,似乎可以使用符号 [][] 来声明一个数组。这看起来与其他语言中的“数组数组”非常相似,所以我想知道它是否是所谓的“锯齿状数组”,每个子数组在内存中独立分配?例如,在下面的代码中,a[0][..]a[1][..] 在内存中不一定是连续的? (我在这里感兴趣的是,由于内存不连续,使用这种[][] 是否可能比[,] 效率低。)

proc test( D1, D2 )
{
    var a: [D1][D2] int;   // "jagged" array?
    var b: [D1, D2] int;   // I assume this is a rectanguar (contiguous) array

    for i in D1 do
    for j in D2 do
        a[i][j] = i * 100 + j;

    for (i, j) in b.domain do
        b[i, j] = i * 100 + j;

    var lo = D1.low, hi = D1.high;

    writeln( "a = ", a );
    writeln( "a[ lo ] = ", a[ lo ] );
    writeln( "a[ hi ] = ", a[ hi ] );
    writeln();
    writeln( "b = ", b );
    writeln( "b[ lo, .. ] = ", b[ lo, .. ] );
    writeln( "b[ hi, .. ] = ", b[ hi, .. ] );
}

test( 0..1, 1..3 );

$ chpl test.chpl
$ ./a.out

a = 1 2 3 101 102 103
a[ lo ] = 1 2 3
a[ hi ] = 101 102 103

b = 1 2 3
101 102 103
b[ lo, .. ] = 1 2 3
b[ hi, .. ] = 101 102 103

一个相关的问题是:是否有任何方法或命令可以知道给定变量或数组元素的内存位置(地址)(以获取有关内存分配的信息)?

【问题讨论】:

    标签: arrays chapel


    【解决方案1】:

    您是正确的,对于 Chapel (var A: […][…] …) 中的数组数组,每个子数组都将独立存储,因此在内存中可能不连续(尽管它们可能是连续的,具体取决于分配器放置它们的位置)。无论它们是否连续,都需要额外的间接访问子数组。

    多维数组 (var A: […, …] …) 的实现由其域映射控制,该域映射控制元素在内存中的存储方式。默认域映射在内存中连续存储多维数组。

    哪种数组形式更高效可能取决于您编写的计算风格和您运行的系统。但是,作为一个具体的例子,如果您要串行访问数组的元素,多维数组通常会由于其连续的内存位置而优于数组数组,正如您所注意到的。

    Chapel 本身不喜欢过多地公开地址,但是如果您依靠一些互操作性功能,您可以确定事物的位置。例如,以下程序使用c_ptrTo() 来获取指向某些数组元素的C 指针,然后使用printf() 将这些位置打印出来:

    use CPtr;
    
    config const n = 3;
    
    var A: [1..n][1..n] real;
    
    var a11 = c_ptrTo(A[1][1]),
        a12 = c_ptrTo(A[1][2]),
        a21 = c_ptrTo(A[2][1]);
    
    var B: [1..n, 1..n] real;
    
    var b11 = c_ptrTo(B[1,1]),
        b12 = c_ptrTo(B[1,2]),
        b21 = c_ptrTo(B[2,1]);
    
    extern proc printf(x...);
    
    printf("%p %p %p\n", a11, a12, a21);
    printf("%p %p %p\n", b11, b12, b21);
    

    【讨论】:

    • W.r.t. “虽然它们可能是,取决于分配器放置它们的位置”:当我使用 n == 2(或我尝试过的其他 2 的幂)运行上面的程序时,我的分配器使两个子数组在内存中连续,导致对我来说有些轻微的恐慌。对于 n == 3,我得到了不连续的地址。当然,您的里程可能会有所不同。
    • W.r.t. “锯齿状阵列”:虽然 Chapel 的设计使得阵列的阵列可用于“锯齿状”/“天际线”阵列(其中子阵列具有不同大小的阵列),但当前实现仅支持内部阵列的情况所有共享同一个域(索引集)。这意味着在实践中,此类阵列目前仍将(实际上)是矩形的。表示真正锯齿状数组的典型解决方法是存储对象数组,其中每个对象都存储自己的域/数组字段——这允许不同的子数组具有不同的大小。
    • 非常感谢!因为我看过一些示例代码,其中 (real, real, real) 之类的元组与数组一起使用(例如,var A: [1..n] 3*real),我想知道我们是否可以做同样的事情“var A:[1..n][1..3] 实数;”因为这更接近其他语言。但是在这里,虽然元组是值类型,所以它们在 A 中可能是连续的,但我不确定 [1..n][1..3] 是否给出了一个连续的数组。所以,速度可能也比“[1..n] 3*real”有点低。我稍后会尝试比较这些。
    • 你说得对,var A: [1..n] 3*real;var A: [1..n] [1..3] real; 在很多方面是相当的。我们当前的实现代码在后端将元组生成为静态 C 数组,因此元组元素的情况最终会在您猜想的连续内存中结束。 Chapel 编译器可能会优化“小型静态大小数组”的情况以使用相同的布局,但我们今天没有。作为两者之间差异的一个示例,如果将 A[i] 传递给期望数组并执行诸如查询其域之类的事情的例程,则这不适用于元组情况。
    猜你喜欢
    • 1970-01-01
    • 2021-06-18
    • 2011-02-05
    • 1970-01-01
    • 2022-12-11
    • 2018-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多