将无符号字符8位转换为实际数字的最快方法

时间:2010-11-15 00:40:54

标签: c++ micro-optimization

我使用unsigned char来存储8个标志。每个标志代表一个立方体的角落。因此00000001将成为角落1 01000100将是角落3和7等。我当前的解决方案是&结果1,2,4,8,16,32,64和128,检查结果是否为零并存储角落。也就是if (result & 1) corners.push_back(1);。我有机会摆脱那个“如果”的陈述吗?我希望我可以通过按位运算符摆脱它,但我想不出任何。

  

关于我为什么要摆脱if语句的一些背景知识。此多维数据集实际上是一个体素,它是网格的一部分,其大小至少为512x512x512。这超过1.34亿体素。我正在对每个体素进行计算(嗯,不完全是,但我不会详细介绍,因为这里不相关),这是很多计算。我需要每帧执行这些计算。每个函数调用的任何速度提升都是微不足道的,这将有助于这些计算量。为了给你一个想法,我的算法(在某些时候)需要确定浮点数是负数,正数还是零(在某些错误内)。我在那里有if语句,比检查更大/更小。我用快速浮点数转换为int函数并将其削减了四分之一秒。目前,128x128x128网格中的每个帧需要4秒多一点。

6 个答案:

答案 0 :(得分:5)

我会完全考虑一种不同的方法:不同的标志组合只有256种可能性。预先计算256个向量并根据需要将其索引。

std::vector<std::vector<int> > corners(256);
for (int i = 0; i < 256; ++i) {
    std::vector<int>& v = corners[i];
    if (i & 1) v.push_back(1);
    if (i & 2) v.push_back(2);
    if (i & 4) v.push_back(4);
    if (i & 8) v.push_back(8);
    if (i & 16) v.push_back(16);
    if (i & 32) v.push_back(32);
    if (i & 64) v.push_back(64);
    if (i & 128) v.push_back(128);
}

for (int i = 0; i < NumVoxels(); ++i) {
    unsigned char flags = GetFlags(i);
    const std::vector& v = corners[flags];

    ... // do whatever with v
}

这样可以避免所有带有push_back call new的条件,我怀疑它会更贵。

答案 1 :(得分:1)

Hackers's Delight,第一页:

x & (-x) // isolates the lowest set bit
x & (x - 1) // clears the lowest set bit

内联push_back方法也会有所帮助(更好地创建一个接收所有标志的函数)。

通常,如果您需要性能,您应该考虑到这一点来设计整个系统。也许如果您发布更多代码,它将更容易提供帮助。

编辑:这是个好主意:

unsigned char LOG2_LUT[256] = {...};
int t;
switch (count_set_bits(flags)){
    case 8:     t = flags; 
                flags &= (flags - 1);       // clearing a bit that was set
                t ^= flags;                 // getting the changed bit
                corners.push_back(LOG2_LUT[t]);
    case 7:     t = flags; 
                flags &= (flags - 1);       
                t ^= flags;                 
                corners.push_back(LOG2_LUT[t]);
    case 6:     t = flags; 
                flags &= (flags - 1);       
                t ^= flags;                 
                corners.push_back(LOG2_LUT[t]);
    // etc...
};

count_set_bits()是一个众所周知的函数:http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable

答案 2 :(得分:1)

如果设置了某些操作需要进行操作,如果没有设置则不需要进行操作,那么您似乎必须在某处具有某种条件。如果它可以某种方式表达为计算,你可以这样解决它,例如:

numCorners = ((result >> 0) & 1) + ((result >> 1) & 1) + ((result >> 2) & 1) + ...

答案 3 :(得分:0)

嗯......我不确定你会比你现在使用的按位运算符快得多。 corner.push_back方法在做什么?

编辑:这应该是评论&gt; _&gt;我的坏......

答案 4 :(得分:0)

有一种方法,它不是“漂亮”,但它有效。

(result & 1)   && corners.push_back(1);
(result & 2)   && corners.push_back(2);
(result & 4)   && corners.push_back(3);
(result & 8)   && corners.push_back(4);
(result & 16)  && corners.push_back(5);
(result & 32)  && corners.push_back(6);
(result & 64)  && corners.push_back(7);
(result & 128) && corners.push_back(8);

它使用C ++语言的一种很少已知的特性:布尔快捷方式。

答案 5 :(得分:0)

我在OpenTTD代码中注意到了类似的算法。事实证明这完全是无用的:你可以通过而不是打破这样的数字来加快速度。相反,通过对字节位的迭代替换现在的vector<>上的迭代。这对缓存更友好。

即。

unsigned char flags = Foo(); // the value you didn't put in a vector<>
for (unsigned char c = (UCHAR_MAX >> 1) + 1; c !=0 ; c >>= 1)
{
  if (flags & c) 
    Bar(flags&c);
}