Jul*_*ius 37 c gcc clang compiler-optimization language-lawyer
我遇到了这样一种情况:对不必要的调用realloc进行优化会很有用.然而,似乎既没有铿锵也没有gcc做这样的事情(Godbolt).- 虽然我看到通过多次调用进行优化malloc.
这个例子:
void *myfunc() {
void *data;
data = malloc(100);
data = realloc(data, 200);
return data;
}
Run Code Online (Sandbox Code Playgroud)
我希望它能够优化到以下内容:
void *myfunc() {
return malloc(200);
}
Run Code Online (Sandbox Code Playgroud)
为什么clang和gcc都没有优化它? - 他们不允许这样做吗?
chu*_*ica 25
他们不被允许这样做吗?
也许,但在这种情况下未完成的优化可能是由于角落功能差异造成的.
如果保留150个字节的可分配内存,则
data = malloc(100); data = realloc(data, 200);返回NULL100个字节消耗(和泄漏)并保留50个字节.
data = malloc(200);返回NULL消耗0个字节(没有泄漏),剩下150个.
这种狭隘情况下的不同功能可能会妨碍优化
编译器是否允许优化realloc?
也许 - 我希望它是允许的.然而,增强编译器以确定何时可能不值得效果.
成功malloc(n); ... realloc(p, 2*n)不同于malloc(2*n);何时...可能设置了一些内存.
它可能超出了编译器的设计...,即使是空码也没有设置任何内存.
sup*_*cat 11
如果作者认为这样做是值得的,那么捆绑其自己的malloc/calloc/free/realloc自包含版本的编译器可以合法地执行指示的优化.链接到外部提供的函数的编译器仍然可以执行这样的优化,如果它记录它不认为对诸如可观察的副作用之类的函数的调用的精确顺序,但是这样的处理可能有点脆弱.
如果在malloc()和realloc()之间没有分配或释放存储,则在执行malloc()时realloc()的大小是已知的,并且realloc()大小大于malloc()大小,则将malloc()和realloc()操作合并为一个更大的分配可能是有意义的.但是,如果内存状态可能在过渡期间发生变化,那么这样的优化可能会导致应该成功的操作失败.例如,给定顺序:
void *p1 = malloc(2000000000);
void *p2 = malloc(2);
free(p1);
p2 = realloc(p2, 2000000000);
Run Code Online (Sandbox Code Playgroud)
在释放p1之前,系统可能没有2000000000字节可用于p2.如果要将代码更改为:
void *p1 = malloc(2000000000);
void *p2 = malloc(2000000000);
free(p1);
Run Code Online (Sandbox Code Playgroud)
这会导致p2的分配失败.因为标准从不保证分配请求会成功,所以这种行为不会不符合要求.另一方面,以下也将是"符合"的实现:
void *malloc(size_t size) { return 0; }
void *calloc(size_t size, size_t count) { return 0; }
void free(void *p) { }
void *realloc(void *p, size_t size) { return 0; }
Run Code Online (Sandbox Code Playgroud)
这样的实现可能被认为比大多数其他实现更"有效",但人们必须相当迟钝地认为它非常有用,除非在极少数情况下,在代码路径上调用上述函数从未执行过
我认为标准显然会允许优化,至少在原始问题中那些简单的情况下.即使在可能导致操作失败但本来可以成功的情况下,标准仍然允许它.最有可能的是,许多编译器没有执行优化的原因是作者认为这些好处不足以证明确定安全和有用的案例所需的努力.
编译器可以优化对被视为纯函数(即没有任何副作用的函数)的函数的多次调用。
所以问题是是否realloc()是纯函数。
C11 标准委员会草案 N1570 对该realloc功能进行了如下说明:
7.22.3.5 realloc 函数 ... 2. 该
realloc函数释放ptr 指向的旧对象,并返回一个指向新对象的指针,该新对象的大小由size 指定。新对象的内容应与释放前旧对象的内容相同,最多为新旧大小中的较小者。新对象中超出旧对象大小的任何字节都具有不确定的值。返回 4。该
realloc函数返回一个指向新对象的指针(可能与指向旧对象的指针具有相同的值),如果无法分配新对象,则返回空指针。
请注意,编译器无法在编译时预测每次调用返回的指针值。
这意味着realloc()不能将其视为纯函数,并且编译器无法优化对它的多次调用。