我需要一些帮助来优化数学重代码的一部分。我已完成分析,并隔离了慢速方法。问题是单独的线路并不慢,但它们被称为很多次,所以我需要在这里捏微秒。
此代码用于在执行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%。
答案 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);