我面临一个有趣的优化问题。
在由大量类组成的大型代码库中,在许多地方经常使用/检查非常量全局(=文件范围)变量的值,并且要避免对该变量进行不必要的内存访问。
这个变量初始化了一次,但是由于它的初始化比较复杂,需要调用很多函数,所以不能这样初始化,在执行之前main()
:
unsigned size = 1000;
int main()
{
// some code
}
或者
unsigned size = CalculateSize();
int main()
{
// some code
}
相反,它必须像这样初始化:
unsigned size;
int main()
{
// some code
size = CalculateSize();
// lots of code (statically/dynamically created class objects, whatnot)
// that makes use of "size"
return 0;
}
仅仅因为size
它不是一个常量并且它是全局的(=文件范围)并且代码又大又复杂,编译器无法推断size
在size = CalculateSize();
. 编译器生成的代码会从变量中获取和重新获取 的值,size
并且不能将其“缓存”在寄存器或本地(堆栈上)变量中,该变量可能与其他经常访问的 CPU 的 d-cache 一起位于局部变量。
因此,如果我有类似以下的内容(出于说明目的而编造的示例):
size = CalculateSize();
if (size > 200) blah1();
blah2();
if (size > 200) blah3();
编译器认为blah1()
并且blah2()
可能会更改size
并生成从size
in读取的内存if (size > 200) blah3();
。
我想随时随地避免额外的阅读。
显然,像这样的黑客:
const unsigned size = 0;
int main()
{
// some code
*(unsigned*)&size = CalculateSize();
// lots more code
}
当他们调用未定义的行为时不会这样做。
问题是如何通知编译器它可以“缓存”size
曾经size = CalculateSize();
执行过的值,并在不调用未定义行为、未指定行为以及希望实现特定行为的情况下执行此操作。
这是C++03和g++ (4.xx)所需要的。C++11可能是也可能不是一个选项,我不确定,我试图避免使用高级/现代 C++ 功能以保持在编码指南和预定义工具集内。
到目前为止,我只提出了一个技巧,可以size
在每个使用它的类中创建一个常量副本并使用该副本,就像这样(decltype
使其成为 C++11,但我们可以不用decltype
):
#include <iostream>
using namespace std;
volatile unsigned initValue = 255;
unsigned size;
#define CACHE_VAL(name) \
const struct CachedVal ## name \
{ \
CachedVal ## name() { this->val = ::name; } \
decltype(::name) val; \
} _CachedVal ## name;
#define CACHED(name) \
_CachedVal ## name . val
class C
{
public:
C() { cout << CACHED(size) << endl; }
CACHE_VAL(size);
};
int main()
{
size = initValue;
C c;
return 0;
}
以上可能只在一定程度上有所帮助。是否有更好、更具启发性的编译器替代方案是合法的 C++?希望有一个最小侵入性(源代码方面)的解决方案。
更新:为了更清楚一点,这是在一个性能敏感的应用程序中。这并不是说我想摆脱对特定变量的不必要读取。我试图让/使编译器产生更优化的代码。任何涉及经常读取/写入另一个变量size
的解决方案以及解决方案中的任何附加代码(尤其是分支和条件分支)执行的频率与size
所提到的一样多,也会影响性能。我不想在一个地方赢,却在另一个地方失去同样甚至更多。
这是一个相关的非解决方案,导致 UB(至少在 C 中)。