帮助优化整数数学

时间:2011-06-20 03:44:59

标签: c# math optimization

我需要一些帮助来优化数学重代码的一部分。我已完成分析,并隔离了慢速方法。问题是单独的线路并不慢,但它们被称为很多次,所以我需要在这里捏微秒。

此代码用于在执行NTSC过滤后转换像素数据。我已经提供了行旁边的分析数据作为总运行时间的百分比,因此您可以看到需要工作的内容。这个功能总共约占我的运行时间的一半(自我占48%,有孩子占53%)。

// byte[] ntscOutput;
// ushort[] filtered; - the pixels are ushort, because of the texture color depth

int f_i = 0;
int row = 0;
for (int i = 0; i < 269440; i++)                                  // 3.77 %
{
    int joined = (ntscOutput[f_i + 1] << 8) + ntscOutput[f_i];    // 6.6 %
    f_i += 2;                                                     // 1.88 %

    filtered[i] = (ushort)joined;                                 // 2.8 %

    ushort red = (ushort)(joined & 0xf800);                       // }
    ushort green = (ushort)(joined & 0x7e0);                      //  > 2.36 % each
    ushort blue = (ushort)(joined & 0x1f);                        // }

    red = (ushort)((red - (red >> 3)) & 0xf800);                  // }
    green = (ushort)((green - (green >> 3)) & 0x7e0);             //  > 4.24 % each
    blue = (ushort)((blue - (blue >> 3)) & 0x1f);                 // }

    filtered[i + 602] = (ushort)(red | green | blue);             // 5.65 %

    row++;
    if (row > 601)
    {
        row = 0;
        i += 602;
    }
}

我愿意接受任何优化方法。如果真的不可能改进实际的数学运算,那么使用不安全代码和指针的东西可能会在操作数组时起作用,以防止这么多的强制转换?也许以某种方式更改我的数组类型,或者可能某种循环展开?我有信心这是可能的,因为过滤操作本身就是一个庞大的C库函数,有大量的循环和数学,整个过程总计占我运行时间的1.35%。

6 个答案:

答案 0 :(得分:5)

您要求进行微优化,但您是否先尝试过宏优化?

269440可被任何2 ^ n整除,这意味着您可以轻松地将此代码线程化为您拥有的处理器数量,并且基本上具有n-tupled的速度。

请确保不要在此代码中声明线程。

使用unchecked关键字进行微优化可能是在rgb块中实现的,通过使用未经检查的{}包围所有内容,但这可能不会有太多帮助。

真正的优化是:

对于joined(ushort)的所有可能值,将filtered[i + 602]的所有结果值存储到一个数组中,每个数组的索引(ushort)已连接,并且不使用计算但直接获取值来自阵列。

然后跳过rgb部分并用作循环体:

filtered[i] = (ushort)joined;
filtered[i+602] = precalculatedValues[(ushort)joined];

你可以通过这种方式将join连接到ushort(删除按位操作后)。

答案 1 :(得分:4)

我想知道,因为你循环269440次(嗯,行变量少),并且滤波变量结果只有2 ^ 16种可能性,你考虑过一个查找表?我不确定2 ^ 16长阵列在C#中的表现如何,但值得一试。

答案 2 :(得分:3)

没有必要拆分加入的ushort并将结果重新组合在一起。 您可以使用

直接计算输出
 filtered[i + 602] = (ushort)(joined - ((joined >> 3) & 0x18e3));            

让我解释一下: 在您的示例中,蓝色使用位0-4,绿色使用5-10,红色使用11-15。连接的位是(以大端顺序排列)

  

rrrr rggg gggb bbbb

加入&gt;&gt; 3将所有组件(红色,格力,蓝色)移动3.即加入&gt;&gt; 3具有格式

  

000r rrrr gggg ggbb

我们希望掩盖来自溢出到另一个组件的一个组件的位。这个完成了 使用0x18e3。即(加入&gt;&gt; 3)&amp; 0x18e3的格式为

  

000r r000 ggg0 00bb

接下来你要减去

joined - ((joined >> 3) & 0x18e3)

这会在一步中从原始组件中减去移位的rgb组件。但是,您必须考虑下溢。即您不希望绿色组件中的下溢传播到红色组件中。一点点思考表明这不是问题。所有移位的分量都小于其原始值,因此不会出现下溢。换句话说,上面的单个陈述应该可以正常工作。 (当然,你仍然需要仔细测试)。

为了获得更高的速度,可以并行对多个像素执行此操作。即如果你在uint64中打包了4个像素,那么同时对所有4个像素执行此操作同样容易。 (但我不确定是否可以很容易地将ushort数组类型转换为uint64数组。)

答案 3 :(得分:2)

你可以一起做红色和蓝色,因为中间的绿色空白区域会阻止互动:

ushort redblue = (ushort)(joined & 0xf81f);
ushort green = (ushort)(joined & 0x7e0);

redblue = (ushort)((redblue - (redblue >> 3)) & 0xf81f);
green = (ushort)((green - (green >> 3)) & 0x7e0);

filtered[i + 602] = (ushort)(redblue | green);

为了提高可读性,您应该为所有这些幻数定义常量。

答案 4 :(得分:1)

我在这个页面上遵循了一些技巧: http://developer.amd.com/documentation/articles/pages/7162004127.aspx

也许其他人会在该页面上找到一些额外的分数?欢迎在下面发表评论。

uint f_i = 0;
uint row = 0;
ushort red, green, blue;
ushort joined;
for (uint i = 0; i < 269440; i++)                                  // 3.77 %
{
    joined = (ntscOutput[f_i + 1] << 8) + ntscOutput[f_i];    // 6.6 %
    f_i += 2;                                                     // 1.88 %

    filtered[i] = joined;                                 // 2.8 %

    red = (joined & 0xf800);                       // }
    green = (joined & 0x7e0);                      //  > 2.36 % each
    blue = (joined & 0x1f);                        // }

    red = (ushort)((red - (red >> 3)) & 0xf800);                  // }
    green = (ushort)((green - (green >> 3)) & 0x7e0);             //  > 4.24 % each
    blue = (ushort)((blue - (blue >> 3)) & 0x1f);                 // }

    filtered[i + 602] = (ushort)(red | green | blue);             // 5.65 %

    row++;
    if (row > 601)
    {
        row = 0;
        i += 602;
    }
}

答案 5 :(得分:0)

(这应与其他答案叠加,例如使用查找表)

是的,您可以使用不安全的方式实现相当大的加速。行设置joined没有理由占6%,循环条件也可以改善。尝试:

int row = 0;
int remaining = 269440/2;
fixed (ushort* p = (ushort*)&ntscOutput[0])
    do {
        int joined = *p;
        p++;

        // ...

    } while (--remaining > 0);