【问题标题】:Swap pointers addresses in C在C中交换指针地址
【发布时间】:2023-05-11 16:15:02
【问题描述】:

我需要对 C 中的两个指针(地址,而不是它们指向的内容)进行超高性能交换。我知道地址是对齐的(16 的倍数)并且是连续的,并且我已经检查过这在每次执行中是否保留。代码如下:

Nodo a __attribute__((aligned(8))), b __attribute__((aligned(4)));
Nodo *nodo_superior __attribute__((aligned(4)));
nodo_superior = &a;
Nodo * nodo_actual __attribute__((aligned(4)));
nodo_actual=&b;
printf("%ld %ld\n", nodo_superior, nodo_actual);

控制台上的结果是这样的(请注意,第一个地址应该“过度”对齐,以便下一个方法起作用):

140594404335200 140594404335216

现在要交换地址,我想更改每个指针的第 5 位:

nodo_superior ^= 16; 
nodo_actual ^= 16;

但是,不幸的是,C 编译器不允许这样做,因为整数不能与指针值混合:这是错误,而不是警告。如果我尝试将指针包含在这样的联合中:

union {
    Nodo * nodo_actual;
    int local_int;
} na;

有这样的想法:

na.local_int ^= 16;

然后性能会降低,因为(我想)如果 nodo_actual 在联合中,编译器就无法对它应用/推断优化。 有什么想法可以强制编译器按照我的意愿行事吗?当然,我可以更改生成的汇编程序,但我认为这不是一个好的选择,

【问题讨论】:

标签: c pointers swap


【解决方案1】:

如果你想切换每个指针的第 5 位,你可以写:

nodo_superior = (Nodo *)((uintptr_t)nodo_superior ^ 16);

uintptr_t 是一个整数类型,(如果存在)保证能够保存指针值而不会丢失信息。

它的目的是通过平面内存模型上明显的同构来完成,所以它应该适合你(尽管先测试一下)。

此外,您应该使用%p 来打印指针,而不是使用%ld,这会导致未定义的行为。打印uintptr_tsee here

【讨论】:

  • 虽然大小不错,但听起来他的编译器通常不允许对指针类型进行按位操作。不过,正如您所说,测试会确认。
  • @donjuedo 这就是为什么你首先将其转换为整数。
【解决方案2】:

使用临时变量以“旧方式”更好地执行交换。

Nodo* Swap= a; a= b; b= Swap;

这需要 2 次读取和 2 次写入。

“智能”解决方案

a^= 16; b^= 16;

需要 2 次读取、2 次异或和 2 次写入。

【讨论】:

  • 我计算了 3 次读取和 3 次写入。您似乎嘲笑的“智能”解决方案是两次读取和两次写入;碰巧异或分解为读取和写入。在任何一种情况下,关闭优化的微基准测试都会得出 xor 版本始终更快,高达 17%。
  • 不,交换变量是临时的并保存在寄存器中。关闭优化的基准测试无助于优化。
  • 公平,使用 -O1,它们无论如何都会编译成相同的一组动作。
【解决方案3】:

我不确定我是否真的理解“交换两个指针”的意思。看起来你有一对节点,你想从另一个节点的地址计算一个节点的地址。

类似的问题是当您有两个数字(例如,玩家编号为 1 和 2)并且想要将玩家编号从 1 更改为 2 时,反之亦然。通常的技巧是计算

  opponent = (1+2) - player ;

你可以用指针玩类似的把戏,因为允许一些算术:

int main(int argc, char **argv)
{
    struct Node x;
    struct Node y;

    printf("x is at %p,  y at %p\n", &x, &y);

    struct Node * one = &x;
    struct Node * other = &x + (&y - one); // (&x + &y) - one

    printf("if one is at %p,  the other is at %p\n", one, other);

    one = &y;
    other = &x + (&y - one);

    printf("if one is at %p,  the other is at %p\n", one, other);

    return 0;
}

【讨论】: