我面临一个有趣的优化问题。
在一个由大量类组成的大型代码库中,许多地方经常使用/检查非常量全局(=文件范围)变量的值,并且应该避免对此变量的不必要内存访问。
这个变量只被初始化一次,但由于其初始化的复杂性和需要调用多个函数,因此不能像这样在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 = CalculateSize();
后面 size
的值不会再次改变。编译器生成的代码需要从变量中获取并重新获取 size
的值,并不能将其“缓存”到寄存器或本地(在堆栈上)变量中,以便与其他经常访问的本地变量一起放在 CPU 的 d-cache 中。所以,如果我有像下面这样的东西(为说明目的而虚构的示例):
size = CalculateSize();
if (size > 200) blah1();
blah2();
if (size > 200) blah3();
编译器认为
blah1()
和blah2()
可能会更改size
,因此在if (size > 200) blah3();
中生成了对size
的内存读取。
我希望尽可能地避免额外的读取。
显然,像这样的黑客行为:
const unsigned size = 0;
int main()
{
// some code
*(unsigned*)&size = CalculateSize();
// lots more code
}
问题在于如何告诉编译器,一旦执行了size = CalculateSize();
,它就可以“缓存”size
的值,而不会引发未定义行为、未指定行为和希望避免的实现特定行为。
这对于C++03和g++ (4.x.x)是必需的。 C++11可能是一个选择,也可能不是一个选择,我不确定,我试图避免使用高级/现代C++功能以符合编码准则和预定义工具集。
到目前为止,我只想出了一个hack,即在每个使用它的类中创建一个常量副本,并使用该副本,类似于以下内容(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
一样频繁执行的任何额外代码(特别是分支和条件分支),也会影响性能。我不想在一个地方赢得胜利,只为在另一个地方输掉同样甚至更多的东西。这里有一个相关的非解决方案,导致UB(至少在C中)。
const_cast
吗?看起来这正是它的完美应用场景。 - Vinícius Gobbo A. de Oliveiraconst_cast
的文档,发现它不能用于这个情况。这会导致未定义的行为,也就是说这不是一个选项。 - Vinícius Gobbo A. de Oliveira