我当时正在阅读,以为一切都很清楚,直到我偶然发现:
同样,大多数实际的Scheme系统使用稍有不同的实现;例如,如果GET_PAIR
减去x
的低位,而不是将它们屏蔽掉,优化器通常能够将该减法与我们所引用的结构成员的偏移相加结合起来,使修改后的指针与未修改的指针一样快。
如何精确地实现这个减法,优化器将如何做它的魔术修改指针一样快的未修改的指针?
最佳答案
本文介绍的技巧是将类型信息编码为8字节对齐指针中未使用的三个最低位。在利用这些信息找出类型之后,
#define PAIR_P(x) (((int) (x) & 7) == 2)
在再次使用指针作为地址之前,必须清除这些额外的位。
#define GET_PAIR(x) ((struct pair *) ((int) (x) & ~7))
注意,在这一点上,我们已经知道了类型,所以我们知道了三个最低有效位的值。它们总是
0b010
(十进制2)。因此,作者建议与其写((int) (x) & ~7)
,不如写((int) (x) - 2)
。如果你这样写代码,if (PAIR_P(x))
{
SCM * thing = GET_PAIR(x)->cdr;
/* Use the thing… */
}
因为我们正在访问
cdr
所指向的struct pair
内的x
成员(在清除较低的位之后),编译器将生成代码来适当地调整指针。像这样的东西。SCM * thing = (SCM *) ((char *)((int) (x) - 2)) + offsetof(struct pair, cdr));
由于整数加减的关联性,我们可以省略一级括号和get(不显示不产生机器代码的外部指针强制转换)
(int) (x) - 2 + offsetof(struct pair, cdr)
其中,
2
和offsetof(struct pair, cdr)
都是编译时常量,可以折叠成单个常量。如果我们要求car
成员(其偏移量为0),这个技巧不会有帮助,但每隔一次帮助也不算太糟。一个现代的优化器可能会自己发现,在我们刚刚测试了
(x & 7) == 2
,x & ~7
等同于x - 2
之后,这个技巧现在可能不再需要了。不过,在你依赖它之前,你想先测量一下。关于c - 什么是低位减法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36988164/