【发布时间】:2014-02-05 22:25:54
【问题描述】:
有时,编译器可以通过对不变量使用模板化内部实现来更好地优化一段代码。例如,如果您在图像中有已知数量的通道,而不是执行以下操作:
Image::doOperation() {
for (unsigned int i = 0; i < numPixels; i++) {
for (unsigned int j = 0; i j mChannels; j++) {
// ...
}
}
}
你可以这样做:
template<unsigned int c> Image::doOperationInternal() {
for (unsigned int i = 0; i < numPixels; i++) {
for (unsigned int j = 0; j < c; j++) {
// ...
}
}
}
Image::doOperation() {
switch (mChannels) {
case 1: doOperation<1>(); break;
case 2: doOperation<2>(); break;
case 3: doOperation<3>(); break;
case 4: doOperation<4>(); break;
}
}
它允许编译器为不同的通道数生成不同的展开循环(这反过来可以极大地提高运行时效率,还可以开启不同的优化,例如 SIMD 指令等)。
但是,这通常可以扩展为一些相当大的 case 语句,并且任何以这种方式优化的方法都必须具有展开的 case 语句。因此,假设我们有一个用于已知图像格式的enum Format(其中枚举的值恰好映射到通道数)。由于枚举只有一定范围的已知值,因此很容易尝试这样做:
template<Image::Format f> Image::doOperationInternal() {
for (unsigned int i = 0; i < numPixels; i++) {
for (unsigned int j = 0; j < static_cast<unsigned int>(f); j++) {
// ...
}
}
}
Image::doOperation() {
const Format f = mFormat;
doOperationInternal<f>();
}
但是,在这种情况下,编译器(正确地)抱怨 f 不是常量表达式,即使它只有一个有限范围,并且理论上编译器可以生成 switch 逻辑来覆盖所有枚举值。
那么,我的问题是:是否有另一种方法可以让编译器生成不变值优化代码,而无需在每次函数调用时进行 switch-case 扩展?
【问题讨论】:
-
我认为你可以使用 boost::variant 之类的东西(即类型安全的联合)。您仍然必须枚举所有要支持的
c值,但该枚举更紧凑。我认为一般的解决方案是代码生成器。 -
@Adam 我不确定 boost::variant 在这种情况下有何帮助...您可以尝试制定一种方法并发布答案吗? :)
-
一个通道应该包含数百万个图像像素,所以我认为循环通过所有通道的开销与处理每个通道的工作量相比可以忽略不计。您是否真的观察到“优化”前后运行时性能的任何实质性差异?
-
@nodakai 是的,这非常重要。和一个共同的优化。如果 A 很大,A*B 在 B. 中是敏感的:同时 A+B 不是。每像素操作中最里面的代码是乘法情况。
-
适度的安装成本是可以的,对吧?每个图像的案例数量像 O(n) 一样吗? O(lg n)具有大常数?或者每个程序执行一次 O(n)? (每个图像 n 最简单,然后每次执行,并且 lg n 解决方案是一团糟)
标签: c++ templates optimization enums