@无名啊,
缓存至寄存器
没有这种操作。
一个变量要么在寄存器,要么在内存,不会同时位于两者。
位于内存的变量只会被缓存到L3/L2/L1 Cache中,不会位于寄存器。而这个缓存操作是CPU自动进行的,不需要程序控制。
所以,变量在不在寄存器,看汇编代码就能知道,不需要运行时确定。
指针本身是否被优化到寄存器与restrict无关。如果不对指针进行取地址操作,它就可以被优化到寄存器。
char buf[16]; char *read_next = buf, *write_next = buf + 16; *write_next = '\0'; // 此时 read_next 岂不就被修改了吗?(假设 read_next 就在 buf 后面)
编译器咋会放心把
read_next
缓存至寄存器的。。
@无名啊,至于43楼的设计到底能不能认为所有权发生了转移,我认为是值得争议的问题。
因为从瞬时来看,每个单独的时刻,所有权都从读指针转移到了写指针。
但从全局来看,所有权在读指针和写指针之间共享。
所以到底算不算转移,可能是“实现定义的”
@老虎会游泳,老虎用
restrict
的情景多吗?我是不是要写完那个小插件,再试试加
restrict
,才理解得更快呢。。
@无名啊,
restrict
针对的是指针指向的内容,不是指针本身。指针本身是否被优化到寄存器与restrict
无关。如果不对指针进行取地址操作,它就可以被优化到寄存器。如果不确定,你可以用gcc -O2 -S
查看汇编代码。
@老虎会游泳,感觉 43 楼的设计,没法加
restrict
?那岂不读指针和结束指针都没法缓存至寄存器,每次读取前都要读一次内存?(因为编译器认为,写指针可能会修改
char *read_next, *end
?)
@无名啊,我知道你想采用的方法。我的观点是如果不确定就不要使用。
注解
restrict 限定符(像寄存器存储类)是有意使用以促进优化的。而从所有组成一致程序的预处理翻译单元中,删除所有此限定符的实例不会影响其含义(即可观的行为)。
编译器可以忽略任何一个或全部使用 restrict 的别名使用暗示。
欲避免未定义行为,程序员应该确保 restrict 限定指针所做的别名引用断言不会违规。此外实现类型转换解引用的另一个方案:
许多编译器提供作为 restrict 对立面的语言扩展:指示即使指针类型不同,也可以别名使用的属性: may_alias (gcc)
@老虎会游泳,画了个草图,容易理解:
@无名啊,有一个实验方法,就是用 gcc -O2 -S 编译一段函数,看看加
restrict
和不加有什么区别。
@无名啊,至于性能问题,据我所知编译器很少生成主动刷新CPU缓存的代码,大部分工作都是交由CPU自动完成的,除非涉及同步原语(信号量、互斥锁等)。
@无名啊,如果问题是阅读理解,我会回答“不行”。
若某个可由 P (直接或间接)访问的对象会被任何手段修改,则该块中所有对该对象(读或写)的访问,都必须经由 P 出现,否则行为未定义。
之前读和之后读都是读。文段中没有体现出时间前后的区别,只强调了定义域的区别。如果读访问发生在不含指针P的块中,则不会有问题。
@老虎会游泳,写指针 和 读指针,都指向同一块缓冲区。只是写指针写入过后的字符对象,都绝不再读取。(因为写指针不会超出读指针,如上所述)
@老虎会游泳,只是【通过
char * restrict
修改后,不再使用其他别名】,也不行?因为解析了一个字符,并写入后,就不再需要读取这个及之前的字符对象了
@无名啊,根据以下文段,我觉得所有权的转移是在声明时发生的,而非使用时发生的。所以只要这个定义域内存在“restrict 指针 P”,就不能通过其他手段访问。
在每个声明了 restrict 指针 P 的块(典型例子是函数体的执行,其中 P 为参数)中,若某个可由 P (直接或间接)访问的对象会被任何手段修改,则该块中所有对该对象(读或写)的访问,都必须经由 P 出现,否则行为未定义。
只有一种情况可以存在其他别名:“restrict 指针 P”指向的内容不会进行任何修改。
若对象决不被修改,则它可以被别名引用,并被异于 restrict 限定的指针访问。
@老虎会游泳,“对象类型”是指非函数类型。。
另外,只是指向
char
类型的读写指针。另外,根据严格别名说的,
char *
可修改任何类型的数据。所以,我认为给
char *
添加restrict
,可防止【写指针写入数据后,编译器认为所有指针的数据缓存都失效不能用了,需要重新读取】。不知这个想法对不对
层主 @老虎会游泳 于 2023-01-27 17:20 删除了该楼层。
@老虎会游泳,我在给
SQLite
写流式解析 csv 的小插件,想只用一个缓冲区存储:数据块、某一行解析好的CSV数据。比如,读取 4K 数据,然后边解析,边往这个缓冲区开头写入。
由于 CSV 数据解析完不会需要更多内存来存储,只会由于转义变得需要更少内存,所以不会发生写指针超出读指针问题。
所以,我在想写指针能不能加
restrict
?加了有没有用?读指针需要加restrict
吗?
@无名啊,对,
restrict
的作用应该是所有权的转移,规则应该和Rust类似:所有权转移给某别名之后,就不能再用其他别名访问了,但是转移之前则可以。
@无名啊,顺便一提,在安卓上long是64位,这就是符号位差异的来源
还有,
memcpy
看起来是最佳选择,因为它没有任何多余的操作——我们想要的就是内存复制,所以我们就应该写内存复制。改成memcpy
后,代码比用联合与指针类型转换都简单。#include <stdio.h> #include <stdint.h> float Q_rsqrt( float number ) { int32_t i = 0; const float threehalfs = 1.5F; float x2 = number * 0.5F; float y = number; memcpy(&i, &y, sizeof(y)); // evil floating point bit level hacking i = 0x5f3759df - ( i >> 1 ); // what the fuck? memcpy(&y, &i, sizeof(y)); y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed return y; } int main() { printf("sizeof(long): %lu\n", sizeof(long)); printf("sizeof(int32_t): %lu\n", sizeof(int32_t)); printf("sizeof(float): %lu\n", sizeof(float)); printf("%0.7f\n", Q_rsqrt(3.14)); printf("%0.7f\n", Q_rsqrt(1024.0)); printf("%0.7f\n", Q_rsqrt(10086.0)); printf("%0.7f\n", Q_rsqrt(2147483647.0)); printf("%0.14f\n", Q_rsqrt(3.14)); printf("%0.14f\n", Q_rsqrt(1024.0)); printf("%0.14f\n", Q_rsqrt(10086.0)); printf("%0.14f\n", Q_rsqrt(2147483647.0)); return 0; }
@老虎会游泳,另外,我这篇帖子,主要是想讨论
restrict
的(因为我已经认下严格别名了)。。我刚才看了看你一开始的其他回复。
我认为,根据 cppreference 对
restrict
的叙述,应该是允许有其他别名的。只要保证:通过
restrict
修饰的指针写入某个对象后,不再使用其他别名访问这个对象,即可?