【问题标题】:Enhance performance to paint image, is SIMD perhapse a solution?提高绘制图像的性能,SIMD 可能是一个解决方案吗?
【发布时间】:2020-02-06 16:21:21
【问题描述】:

我没有使用 SIMD 的经验,但是有一种方法太慢了。我知道获得 40fps,我需要更多。 有谁知道我怎样才能使这种绘画方法更快?也许 SIMD 指令是一种解决方案?

sourceData 现在是 byte[] (videoBytes) 但也可以使用指针。

 public bool PaintFrame(IntPtr layerBuffer, ushort vStart, byte vScale)
    {
        for (ushort y = 0; y < height; y++)
        {
            ushort eff_y = (ushort)(vScale * (y - vStart) / 128);

            var newY = tileHeight > 0 ? eff_y % tileHeight : 0;
            uint y_add = (uint)(newY * tileWidth * bitsPerPixel >> 3);

            for (int x = 0; x < width; x++)
            {
                var newX = tileWidth > 0 ? x % tileWidth : 0;

                ushort x_add = (ushort)(newX * bitsPerPixel >> 3);
                uint tile_offset = y_add + x_add;
                byte color = videoBytes[tile_offset];
                var colorIndex = BitsPerPxlCalculation(color, newX);

                // Apply Palette Offset
                if (paletteOffset > 0)
                    colorIndex += paletteOffset;
                var place = x + eff_y * width;
                Marshal.WriteByte(layerBuffer + place, colorIndex);
            }
        }
        return true;
    }


    private void UpdateBitPerPixelMethod()
    {
        // Convert tile byte to indexed color
        switch (bitsPerPixel)
        {
            case 1:
                BitsPerPxlCalculation = (color, newX) => color;
                break;
            case 2:
                BitsPerPxlCalculation = (color, newX) => (byte)(color >> 6 - ((newX & 3) << 1) & 3);
                break;
            case 4:
                BitsPerPxlCalculation = (color, newX) => (byte)(color >> 4 - ((newX & 1) << 2) & 0xf);
                break;
            case 8:
                BitsPerPxlCalculation = (color, newX) => color;
                break;
        }
    }

更多信息

根据设置,可以更改 bpp。索引颜色和调色板颜色分开存储。这里我必须重新创建图像像素索引,所以稍后我使用 WPF(Windows) 或 SDL(Linux, Mac) 中的调色板和颜色索引来显示图像。

vStart 是在顶部裁剪图像的能力。

UpdateBitPerPixelMethod() 在帧渲染期间不会改变,只会在之前改变。在for期间,不能更改任何设置数据。

所以我希望某些部分可以用 SIMD 编写,因为所有像素的过程都是相同的。

【问题讨论】:

  • Marshal.WriteByte 一定是一个瓶颈,因为它被调用了数百万次。您可以尝试减少 Marshall.* 调用,例如使用一个 Marshal.Copy 而不是一百万个 Marshal.WriteByte
  • 这太笼统了,太多“奇怪的东西”正在发生。一些特殊情况可以转化为 SIMD 代码。
  • @Renat 比 Marchal.WriteByte 快一个 byte[] 吗?
  • @harold ,您能说明一下可以更改的部分吗?我添加了更多信息。

标签: c# performance paint simd


【解决方案1】:

喂,

您的代码对我来说不是最清楚的。你想创建一个新的矩阵/图像吗?如果是,则创建一个新的 2D 分配并将整个图像计算到其中。不再需要计算后将其设置为 0。 将 Marshal.WriteByte(layerBuffer + place, colorIndex); 替换为 2D 图像(也许这是图像?)。

关于其余部分,这是一个问题,因为您在索引和跳转中存在不统一的偏移量。这将使开发 SIMD 解决方案变得困难(您需要掩蔽和其他东西)。我的赌注是计算所有索引的所有内容并将其保存到单独的 2D 矩阵中,这些矩阵在开始时分配一次。
例如:

ushort eff_y = (ushort)(vScale * (y - vStart) / 128);

按每个图像行计算。现在你可以将它作为一个数组计算一次,因为我不相信图像的格式大小在运行期间会发生变化。

我不知道在程序启动时是否将 vStart 和 vScale 定义为常量。您应该对使用常量的每个计算都执行此操作,并稍后读取矩阵进行计算。

SIMD 可以提供帮助,但前提是您每次迭代都计算相同的东西,并且避免分支和切换案例。

加法1

从我的角度来看,您有多个问题和设计考虑。 首先,您需要摆脱 SIMD 会对您的情况有所帮助的想法。您需要删除所有条件语句。 SIMD-s 不是为处理条件语句而构建的。

您的想法应该是将逻辑拆分为可管理的部分,这样您就可以看到部分代码花费的时间最多。 一个大问题是编组中的写入字节,这会自动告诉编译器您只处理并且专门处理 1 个字节。我猜这会造成大瓶颈。

通过代码分析,我在每个循环中看到您正在进行检查。这必须重组。

假设图像很少被裁剪,这将与图像计算分离。

List<ushort> eff_y = new List<ushort>();
List<uint> y_add = new List<uint>();
for (ushort y = 0; y < height; y++)
{
    eff_y.add((ushort)(vScale * (y - vStart) / 128));
    var newY = tileHeight > 0 ? eff_y % tileHeight : 0;
    y_add = (uint)(newY * tileWidth * bitsPerPixel >> 3);
}

因此,只有在裁剪发生变化时才能预先计算和更改。

现在变得非常棘手。

paletteOffset - if 语句只在paletteOffset 中有意义可以是负数,然后将其归零并删除 if 语句

bitsPerPixel - 这看起来像是渲染持续时间的固定值 所以删除 UpdateBitPerPixelMethod 并发送一个参数。

for (ushort y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        var newX = tileWidth > 0 ? x % tileWidth : 0; // conditional stetement

        ushort x_add = (ushort)(newX * bitsPerPixel >> 3);
        uint tile_offset = y_add + x_add;
        byte color = videoBytes[tile_offset];
        var colorIndex = BitsPerPxlCalculation(color, newX);

        // Apply Palette Offset
        if (paletteOffset > 0) // conditional stetement
            colorIndex += paletteOffset;
        var place = x + eff_y * width;
        Marshal.WriteByte(layerBuffer + place, colorIndex);
    }
}

这只是在您尝试使用 SIMD 之前需要完成的几件事。但是到那时,这些更改将为编译器提供有关您想要做什么的提示。这可以改善机器代码的执行。您还需要测试代码的性能,以找出很难通过代码正确假设或猜测的瓶颈。

祝你好运

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-05
    • 2010-12-02
    • 1970-01-01
    相关资源
    最近更新 更多