【问题标题】:ASM access dynamic 2d array of c structuresASM 访问 c 结构的动态二维数组
【发布时间】:2017-04-13 17:14:12
【问题描述】:

免得假设我有一个类似的结构

struct color {
    int16_t r;
    int16_t g;
    int16_t b;
};

还有一个点数组

struct color** colors = (struct color**) malloc(rows*sizeof(struct color*));
for (int i=0; i < rows; i++) {
    colors[i] = (struct color*) malloc(columns*sizeof(struct color));
    for (int j=0; j < columns; j++) {
        colors[i][j].r=0;
        colors[i][j].g=0;
        colors[i][j].b=0;
    }
}

我想把它传递给一个函数

void some_function(struct color** colors);

颜色现在在 rdi 上。我想在 assambler 中迭代该数组并将 1 添加到 r、2 到 g 和 3 到 b。我知道 rdi 是指向颜色的指针,[rdi] 是指向颜色 [0] 的指针,[rdi + 8] 是指向颜色 [1] 的指针,但是如何访问颜色 [1][2].r 和颜色 [1 ][2].g

【问题讨论】:

  • 取消引用更多并应用适当的偏移量。如果您使用指针而不是索引重写 C 代码,您可能会更好地理解它,然后您可以将其直接转换为 asm。 (不要忘记 C 指针算术会自动缩放,但在 asm 中您使用字节偏移量。)
  • C 代码中没有二维数组,注意哪个可以指向一个或代表一个!指针不是数组。
  • OTOH 用 C 定义的二维数组将以“平面”内存模型结尾,因此它不能与他的解决方案互换。但我同意,他有指向行的指针列表。奇怪的低效设计。
  • 你有没有考虑在 gcc 中使用 -S 选项并查看它生成的 asm 代码?
  • 如果您关心性能,您可能需要将指向行的指针数组重新设计为单个平面数组。 SSE2 / AVX2 将在一个大缓冲区而不是每行单独的数组上工作得更好。

标签: c arrays assembly nasm


【解决方案1】:

[rdi]是指向colors[0]的指针,[rdi + 8]是指向colors[1]的指针,但是如何访问colors[1][2].r和colors[1][2].g

mov  rsi,[rdi + row*8]  ; rsi = pointer to single row data

mov  ax,[rsi + column*size_of_color + offset_of_r/g/b] ; value of r/g/b

rdi 的使用来看,我猜你正在处理 64b 代码。

所以我猜color 结构是 8B 对齐的,所以它的大小可能是 24B,[r, g, b] 的偏移量可能是 [+0, +8, +16]

所以要从 [13][7] 颜色中选择“b”值,代码将是:

mov rsi,[rdi + 13*8]
mov eax,7 * 24
mov ax,[rsi + eax + 16] ; ax = "colors[13][7].b"

为什么我对结构大小/偏移使用“猜测”和“可能”之类的词。因为这取决于 C 编译器的实现和配置。我相信您确实想要的是使结构紧密的内存打包(即大小 6B 和偏移量 [+0, +2, +4])。

搜索你的编译器指令来实现这一点,这里有一些pragma documentation for gcc,这里有attributes

struct color {
    int16_t r;
    int16_t g;
    int16_t b;
    // if you are not short of memory, consider to bump the size to 8B
    // int16_t padding2B;   // *8 multiplier is available in ASM addressing
                            // like mov ax,[rsi + ebx*8 + 2]
} __attribute__ ((packed));
// gcc will now compile this as 6B structure
// Performance warning:
// also it will stop assume anything about alignment
// so the C compiled machine code can be less optimal
// than for "unpacked" struct

正如您已经意识到的,二维数组在 ASM 中是 PITA,那么您为什么不采用平面内存结构呢?尤其是在固定列大小为 2 的幂次方的情况下,平面内存结构不仅更易于编码,而且还可以通过将行置于连续内存中而获得可观的性能提升。您的解决方案取决于malloc 分配策略,并且对于碎片堆,您可能会以不同缓存页面中的每一行颜色结束,有几个额外的缓存未命中(比平面结构)。

struct color* colors = (struct color*) malloc(rows*columns*sizeof(struct color));
colors[row * columns + column].r = 0;

那么对于 asm,你不仅要提供指针,还要提供 columns 的大小(我看不出对于任何有意义的事情都可以省略 rows)。


最后一点。为什么不简单地用 C 语言编写一些访问该结构的示例,编译它并检查生成的汇编程序,以了解如何访问该结构的成员(它也可能使用一些技巧来执行这些 *24/etc目标平台上的快速方式)。

【讨论】:

  • I guess the color structure is 8B aligned 为什么会这样?它是 2 字节对齐的,它的大小是 6。
  • @Jester 因为我对另一个平台感到困惑 + 具有不同的默认值。另外,每当我忘记为我的程序集显式指定任何接口结构时,它总是适得其反(但我再一次从未将它们用于由相同类型的变量组成的结构)。你是对的,godbolt 默认显示它是 6B 大小和 2B 对齐。
  • 不会 struct color* colors = (struct color*) malloc(rowscolumnssizeof(struct color));颜色[行 * 列 + 列].r = 0;产生一维数组?我可以像 colors[i][j] 一样在 c 中访问它吗?
  • @MichałPiotrStankiewicz 不,你不能,你必须通过一维索引 [row * colums + column] 访问它(或任何其他映射函数,但为了性能,你想逐个元素地访问它,不要跳跃太多)。为什么要两个 [][]?如果它是真正的 2D C++ 数组,它无论如何都会编译成 1D 平面内存。您的指针列表无法做到这一点。
猜你喜欢
  • 2021-04-21
  • 2014-11-13
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-25
  • 2012-02-28
  • 2021-09-22
相关资源
最近更新 更多