当前位置:首页 > 其他 > 正文内容

[C#] 对24位图画进行水平翻转(FlipX)的跨渠道SIMD硬件加速向量算法(运用YShuffleX3Kernel)

邻居的猫1个月前 (12-09)其他228

在上一篇文章里,给咱们讲解了32位图画水平翻转(FlipX)算法,所以本文来讨论愈加杂乱的24位图画水平翻转算法。
本文除了会给出标量算法外,还会给出向量算法。且这些算法是跨渠道的,同一份源代码,能在 X86(Sse、Avx等指令集)及Arm(AdvSimd等指令集)等架构上运转,且均享有SIMD硬件加速。

一、标量算法

1.1 算法完成

标量算法对24位图画的处理,与32位图画十分类似,仅 cbPixel 的值不同。

源代码如下。

public static unsafe void ScalarDoBatch(byte* pSrc, int strideSrc, int width, int height, byte* pDst, int strideDst) {
    const int cbPixel = 3; // 24 bit: Bgr24, Rgb24.
    byte* pRow = pSrc;
    byte* qRow = pDst;
    for (int i = 0; i < height; i++) {
        byte* p = pRow + (width - 1) * cbPixel;
        byte* q = qRow;
        for (int j = 0; j < width; j++) {
            for (int k = 0; k < cbPixel; k++) {
                q[k] = p[k];
            }
            p -= cbPixel;
            q += cbPixel;
        }
        pRow += strideSrc;
        qRow += strideDst;
    }
}

1.2 基准测验代码

运用 BenchmarkDotNet 进行基准测验。

[Benchmark(Baseline = true)]
public void Scalar() {
    ScalarDo(_sourceBitmapData, _destinationBitmapData, false);
}

//[Benchmark]
public void ScalarParallel() {
    ScalarDo(_sourceBitmapData, _destinationBitmapData, true);
}

public static unsafe void ScalarDo(BitmapData src, BitmapData dst, bool useParallel = false) {
    int width = src.Width;
    int height = src.Height;
    int strideSrc = src.Stride;
    int strideDst = dst.Stride;
    byte* pSrc = (byte*)src.Scan0.ToPointer();
    byte* pDst = (byte*)dst.Scan0.ToPointer();
    bool allowParallel = useParallel && (height > 16) && (Environment.ProcessorCount > 1);
    if (allowParallel) {
        Parallel.For(0, height, i => {
            int start = i;
            int len = 1;
            byte* pSrc2 = pSrc + start * (long)strideSrc;
            byte* pDst2 = pDst + start * (long)strideDst;
            ScalarDoBatch(pSrc2, strideSrc, width, len, pDst2, strideDst);
        });
    } else {
        ScalarDoBatch(pSrc, strideSrc, width, height, pDst, strideDst);
    }
}

二、向量算法

2.1 算法思路

2.1.1 难点阐明

24位像素的标量算法改的很简单,可是24位像素的向量算法要杂乱的多。

这是由于向量巨细一般是 16或32字节这样的2的整数幂,而24位像素是3个字节一组,无法整除。这就给地址核算、数据处理等方面,带来很大的难题。

2.1.2 处理办法:每次处理3个向量

已然1个向量无法被3整除,那么咱们爽性用3个向量。这样肯定能被3整除。

例如运用Sse指令集时,向量巨细为128位,即16个字节。3个向量,便是 48字节,正好能放下16个 24位像素。

随后边临一个难点——怎样对3个向量内的24位像素进行翻转?

依据前一篇文章的经历,处理1个向量内翻转时,能够运用Shuffle办法,只需结构好索引就行。现在面临3个向量,若有适用于3个向量的换位办法就好了。

为了处理这一难题,VectorTraits库供给了YShuffleX3等办法。且由于能保证索引总是在有用范围内,故还能够运用功能更好的 YShuffleX3Kernel 办法。

在大多数时分,YShuffleX3Kernel 是运用单向量的shuffle指令组合而成。由于 .NET 8.0 增加了一批“多向量换位”的硬件指令,所以在以下渠道,能取得更好的硬件加速。

  • Arm: .NET 8.0 新增了对 AdvSimd指令集里的“2-4向量查表”指令的支撑。例如 vqtbl3q_u8.
  • X86: .NET 8.0 新增了对 Avx512系列指令集的支撑,而它供给了“2向量重排”的指令。例如 _mm_permutex2var_epi8.

详见 [C#] .NET8增加了Arm架构的多寄存器的查表函数(VectorTableLookup/VectorTableLookupExtension)。

YShuffleX3 在 .NET Framework 等渠道上运转时是没有硬件加速的,这是由于这些渠道不支撑Sse等向量指令。能够经过 Vectors 的 YShuffleX3Kernel_AcceleratedTypes 特点来得知哪些元素类型有硬件加速。当发现不支撑时,宜切换为标量算法。

别的,还能够经过 Vectors.Instance.UsedInstructionSets 来检查该向量所运用的指令集。

2.1.3 用YShuffleX3Kernel对3个向量内的24位像素进行翻转

为了便于跨渠道,这儿运用了主动巨细向量Vector。且由于它的巨细不固定,所以需求写个循环来核算索引。依据上一篇文章的经历,咱们能够在类的静态结构办法里做这个核算。

private static readonly Vector<byte> _shuffleIndices0;
private static readonly Vector<byte> _shuffleIndices1;
private static readonly Vector<byte> _shuffleIndices2;

static ImageFlipXOn24bitBenchmark() {
    const int cbPixel = 3; // 24 bit: Bgr24, Rgb24.
    int vectorWidth = Vector<byte>.Count;
    int blockSize = vectorWidth * cbPixel;
    Span<byte> buf = stackalloc byte[blockSize];
    for (int i = 0; i < blockSize; i++) {
        int m = i / cbPixel;
        int n = i % cbPixel;
        buf[i] = (byte)((vectorWidth - 1 - m) * cbPixel + n);
    }
    _shuffleIndices0 = Vectors.Create(buf);
    _shuffleIndices1 = Vectors.Create(buf.Slice(vectorWidth * 1));
    _shuffleIndices2 = Vectors.Create(buf.Slice(vectorWidth * 2));
}

由于现在是需求对3个向量核算索引,故能够运用栈分配,创立一个3倍向量宽度的buf。核算好索引后,能够运用Span的Slice办法,别离加载这3个索引向量。

索引核算好后,便能够用 YShuffleX3Kernel 来对3个向量做换位了。

temp0 = Vectors.YShuffleX3Kernel(data0, data1, data2, _shuffleIndices0);
temp1 = Vectors.YShuffleX3Kernel(data0, data1, data2, _shuffleIndices1);
temp2 = Vectors.YShuffleX3Kernel(data0, data1, data2, _shuffleIndices2);

随后便可参考上一篇文章的思路,对整个图画进行水平翻转。

2.2 算法完成

依据上面的思路,编写代码。源代码如下。

public static unsafe void UseVectorsDoBatch(byte* pSrc, int strideSrc, int width, int height, byte* pDst, int strideDst) {
    const int cbPixel = 3; // 24 bit: Bgr24, Rgb24.
    Vector<byte> indices0 = _shuffleIndices0;
    Vector<byte> indices1 = _shuffleIndices1;
    Vector<byte> indices2 = _shuffleIndices2;
    int vectorWidth = Vector<byte>.Count;
    if (width <= vectorWidth) {
        ScalarDoBatch(pSrc, strideSrc, width, height, pDst, strideDst);
        return;
    }
    int maxX = width - vectorWidth;
    byte* pRow = pSrc;
    byte* qRow = pDst;
    for (int i = 0; i < height; i++) {
        Vector<byte>* pLast = (Vector<byte>*)pRow;
        Vector<byte>* qLast = (Vector<byte>*)(qRow + maxX * cbPixel);
        Vector<byte>* p = (Vector<byte>*)(pRow + maxX * cbPixel);
        Vector<byte>* q = (Vector<byte>*)qRow;
        for (; ; ) {
            Vector<byte> data0, data1, data2, temp0, temp1, temp2;
            // Load.
            data0 = p[0];
            data1 = p[1];
            data2 = p[2];
            // FlipX.
            temp0 = Vectors.YShuffleX3Kernel(data0, data1, data2, indices0);
            temp1 = Vectors.YShuffleX3Kernel(data0, data1, data2, indices1);
            temp2 = Vectors.YShuffleX3Kernel(data0, data1, data2, indices2);
            // Store.
            q[0] = temp0;
            q[1] = temp1;
            q[2] = temp2;
            // Next.
            if (p <= pLast) break;
            p -= cbPixel;
            q += cbPixel;
            if (p < pLast) p = pLast; // The last block is also use vector.
            if (q > qLast) q = qLast;
        }
        pRow += strideSrc;
        qRow += strideDst;
    }
}

2.3 基准测验代码

随后为该算法编写基准测验代码。

[Benchmark]
public void UseVectors() {
    UseVectorsDo(_sourceBitmapData, _destinationBitmapData, false);
}

//[Benchmark]
public void UseVectorsParallel() {
    UseVectorsDo(_sourceBitmapData, _destinationBitmapData, true);
}

public static unsafe void UseVectorsDo(BitmapData src, BitmapData dst, bool useParallel = false) {
    int vectorWidth = Vector<byte>.Count;
    int width = src.Width;
    int height = src.Height;
    if (width <= vectorWidth) {
        ScalarDo(src, dst, useParallel);
        return;
    }
    int strideSrc = src.Stride;
    int strideDst = dst.Stride;
    byte* pSrc = (byte*)src.Scan0.ToPointer();
    byte* pDst = (byte*)dst.Scan0.ToPointer();
    bool allowParallel = useParallel && (height > 16) && (Environment.ProcessorCount > 1);
    if (allowParallel) {
        Parallel.For(0, height, i => {
            int start = i;
            int len = 1;
            byte* pSrc2 = pSrc + start * (long)strideSrc;
            byte* pDst2 = pDst + start * (long)strideDst;
            UseVectorsDoBatch(pSrc2, strideSrc, width, len, pDst2, strideDst);
        });
    } else {
        UseVectorsDoBatch(pSrc, strideSrc, width, height, pDst, strideDst);
    }
}

2.4 运用 YShuffleX3Kernel_Args 来做进一步的优化

跟上篇文章所说的 YShuffleKernel 相同,YShuffleX3Kernel 也供给了Args、Core后缀的办法。这用这些办法,能够将部分运算从循环内,挪至循环前,然后提高了功能。

源代码如下。

public static unsafe void UseVectorsArgsDoBatch(byte* pSrc, int strideSrc, int width, int height, byte* pDst, int strideDst) {
    const int cbPixel = 3; // 24 bit: Bgr24, Rgb24.
    Vectors.YShuffleX3Kernel_Args(_shuffleIndices0, out var indices0arg0, out var indices0arg1, out var indices0arg2, out var indices0arg3);
    Vectors.YShuffleX3Kernel_Args(_shuffleIndices1, out var indices1arg0, out var indices1arg1, out var indices1arg2, out var indices1arg3);
    Vectors.YShuffleX3Kernel_Args(_shuffleIndices2, out var indices2arg0, out var indices2arg1, out var indices2arg2, out var indices2arg3);
    int vectorWidth = Vector<byte>.Count;
    if (width <= vectorWidth) {
        ScalarDoBatch(pSrc, strideSrc, width, height, pDst, strideDst);
        return;
    }
    int maxX = width - vectorWidth;
    byte* pRow = pSrc;
    byte* qRow = pDst;
    for (int i = 0; i < height; i++) {
        Vector<byte>* pLast = (Vector<byte>*)pRow;
        Vector<byte>* qLast = (Vector<byte>*)(qRow + maxX * cbPixel);
        Vector<byte>* p = (Vector<byte>*)(pRow + maxX * cbPixel);
        Vector<byte>* q = (Vector<byte>*)qRow;
        for (; ; ) {
            Vector<byte> data0, data1, data2, temp0, temp1, temp2;
            // Load.
            data0 = p[0];
            data1 = p[1];
            data2 = p[2];
            // FlipX.
            //temp0 = Vectors.YShuffleX3Kernel(data0, data1, data2, _shuffleIndices0);
            //temp1 = Vectors.YShuffleX3Kernel(data0, data1, data2, _shuffleIndices1);
            //temp2 = Vectors.YShuffleX3Kernel(data0, data1, data2, _shuffleIndices2);
            temp0 = Vectors.YShuffleX3Kernel_Core(data0, data1, data2, indices0arg0, indices0arg1, indices0arg2, indices0arg3);
            temp1 = Vectors.YShuffleX3Kernel_Core(data0, data1, data2, indices1arg0, indices1arg1, indices1arg2, indices1arg3);
            temp2 = Vectors.YShuffleX3Kernel_Core(data0, data1, data2, indices2arg0, indices2arg1, indices2arg2, indices2arg3);
            // Store.
            q[0] = temp0;
            q[1] = temp1;
            q[2] = temp2;
            // Next.
            if (p <= pLast) break;
            p -= cbPixel;
            q += cbPixel;
            if (p < pLast) p = pLast; // The last block is also use vector.
            if (q > qLast) q = qLast;
        }
        pRow += strideSrc;
        qRow += strideDst;
    }
}

三、基准测验成果

3.1 X86 架构

3.1.1 X86 架构上.NET 6.0程序的测验成果。

X86架构上.NET 6.0程序的基准测验成果如下。

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4541/23H2/2023Update/SunValley3)
AMD Ryzen 7 7840H w/ Radeon 780M Graphics, 1 CPU, 16 logical and 8 physical cores
.NET SDK 8.0.403
  [Host]     : .NET 6.0.35 (6.0.3524.45918), X64 RyuJIT AVX2
  DefaultJob : .NET 6.0.35 (6.0.3524.45918), X64 RyuJIT AVX2


| Method         | Width | Mean        | Error     | StdDev    | Ratio | RatioSD | Code Size |
|--------------- |------ |------------:|----------:|----------:|------:|--------:|----------:|
| Scalar         | 1024  |  1,110.8 us |  21.74 us |  22.33 us |  1.00 |    0.03 |   2,053 B |
| UseVectors     | 1024  |    492.3 us |   9.74 us |  15.72 us |  0.44 |    0.02 |   4,505 B |
| UseVectorsArgs | 1024  |    238.9 us |   3.14 us |   2.94 us |  0.22 |    0.00 |   4,234 B |
|                |       |             |           |           |       |         |           |
| Scalar         | 2048  |  4,430.0 us |  87.93 us |  94.08 us |  1.00 |    0.03 |   2,053 B |
| UseVectors     | 2048  |  2,319.6 us |  18.62 us |  17.41 us |  0.52 |    0.01 |   4,505 B |
| UseVectorsArgs | 2048  |  1,793.2 us |  34.57 us |  33.95 us |  0.40 |    0.01 |   4,234 B |
|                |       |             |           |           |       |         |           |
| Scalar         | 4096  | 16,536.4 us | 329.23 us | 618.37 us |  1.00 |    0.05 |   2,053 B |
| UseVectors     | 4096  |  9,040.4 us | 104.73 us |  97.96 us |  0.55 |    0.02 |   4,490 B |
| UseVectorsArgs | 4096  |  6,728.0 us | 120.28 us | 133.69 us |  0.41 |    0.02 |   4,219 B |
  • Scalar: 标量算法。
  • UseVectors: 向量算法。
  • UseVectorsArgs: 运用Args将部分运算挪至循环前的向量算法。

以1024时的测验成果为例,来调查向量化算法比起标量算法的功能提高。

  • UseVectors:1,110.8/492.3 ≈ 2.26。即功能提高了 2.26 倍。
  • UseVectorsArgs:1,110.8/238.9 ≈4.65。即功能提高了 4.65 倍。

将程序的输出信息翻到最前面,留意看这2行信息。

Vectors.Instance:       VectorTraits256Avx2     // Avx, Avx2, Sse, Sse2
YShuffleX3Kernel_AcceleratedTypes:      SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double
  • Vectors.Instance: Vectors 用的是哪一套完成。“VectorTraits256Avx2”表明是256位Avx2指令集的完成。且它右侧的“//”后边,给出了已运用指令集的称号列表。例如现在是 Avx, Avx2, Sse, Sse2. (由于在拼装256位向量时,有时需运用128位向量,故也运用了 Sse、Sse2 指令集)。
  • YShuffleX3Kernel_AcceleratedTypes: YShuffleX3Kernel的哪些元素类型有硬件加速。上面的代码运用的是Byte类型,而该特点含有Byte类型,故上面的代码中的YShuffleX3Kernel是有硬件加速的。

为了便利咱们调查所运用的指令集、是否有硬件极速,后边会将这2行信息放在基准测验成果前,一同展现。

3.1.2 X86 架构上.NET 7.0程序的测验成果。

X86架构上.NET 7.0程序的基准测验成果如下。

Vectors.Instance:       VectorTraits256Avx2     // Avx, Avx2, Sse, Sse2
YShuffleX3Kernel_AcceleratedTypes:      SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4541/23H2/2023Update/SunValley3)
AMD Ryzen 7 7840H w/ Radeon 780M Graphics, 1 CPU, 16 logical and 8 physical cores
.NET SDK 8.0.403
  [Host]     : .NET 7.0.20 (7.0.2024.26716), X64 RyuJIT AVX2
  DefaultJob : .NET 7.0.20 (7.0.2024.26716), X64 RyuJIT AVX2


| Method         | Width | Mean        | Error     | StdDev    | Ratio | RatioSD | Code Size |
|--------------- |------ |------------:|----------:|----------:|------:|--------:|----------:|
| Scalar         | 1024  |  1,120.3 us |  22.39 us |  25.78 us |  1.00 |    0.03 |   1,673 B |
| UseVectors     | 1024  |    236.7 us |   4.63 us |   5.69 us |  0.21 |    0.01 |   3,724 B |
| UseVectorsArgs | 1024  |    209.5 us |   4.00 us |   4.45 us |  0.19 |    0.01 |   4,031 B |
|                |       |             |           |           |       |         |           |
| Scalar         | 2048  |  4,431.6 us |  65.38 us |  61.16 us |  1.00 |    0.02 |   1,673 B |
| UseVectors     | 2048  |  1,866.8 us |  36.26 us |  48.41 us |  0.42 |    0.01 |   3,724 B |
| UseVectorsArgs | 2048  |  1,889.9 us |  37.54 us |  74.97 us |  0.43 |    0.02 |   4,031 B |
|                |       |             |           |           |       |         |           |
| Scalar         | 4096  | 16,617.9 us | 329.75 us | 559.94 us |  1.00 |    0.05 |   1,673 B |
| UseVectors     | 4096  |  6,337.2 us |  62.08 us |  55.03 us |  0.38 |    0.01 |   3,709 B |
| UseVectorsArgs | 4096  |  6,408.1 us | 126.27 us | 118.11 us |  0.39 |    0.01 |   4,016 B |

以1024时的测验成果为例,来调查向量化算法比起标量算法的功能提高。

  • UseVectors:1,120.3/236.7 ≈ 4.73。
  • UseVectorsArgs:1,120.3/209.5 ≈5.35。

此刻能够留意到,UseVectors与UseVectorsArgs的功能距离不大了。这是由于从 .NET 7.0 开端,即时编译器(JIT)会做优化,主动将循环内的重复运算挪至循环。故造成了距离不大的现象。

3.1.3 X86 架构上.NET 8.0程序的测验成果。

X86架构上.NET 8.0程序的基准测验成果如下。

Vectors.Instance:       VectorTraits256Avx2     // Avx, Avx2, Sse, Sse2, Avx512VL
YShuffleX3Kernel_AcceleratedTypes:      SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4541/23H2/2023Update/SunValley3)
AMD Ryzen 7 7840H w/ Radeon 780M Graphics, 1 CPU, 16 logical and 8 physical cores
.NET SDK 8.0.403
  [Host]     : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  DefaultJob : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI


| Method         | Width | Mean        | Error      | StdDev     | Ratio | RatioSD |
|--------------- |------ |------------:|-----------:|-----------:|------:|--------:|
| Scalar         | 1024  |   549.22 us |  10.876 us |  11.637 us |  1.00 |    0.03 |
| UseVectors     | 1024  |    68.21 us |   1.326 us |   2.142 us |  0.12 |    0.00 |
| UseVectorsArgs | 1024  |    68.71 us |   1.360 us |   2.453 us |  0.13 |    0.01 |
|                |       |             |            |            |       |         |
| Scalar         | 2048  | 2,704.83 us |  53.643 us |  92.531 us |  1.00 |    0.05 |
| UseVectors     | 2048  | 1,014.52 us |   8.824 us |   7.822 us |  0.38 |    0.01 |
| UseVectorsArgs | 2048  | 1,020.66 us |  15.739 us |  14.723 us |  0.38 |    0.01 |
|                |       |             |            |            |       |         |
| Scalar         | 4096  | 9,778.60 us | 114.022 us | 106.656 us |  1.00 |    0.01 |
| UseVectors     | 4096  | 4,360.43 us |  60.832 us |  56.903 us |  0.45 |    0.01 |
| UseVectorsArgs | 4096  | 4,341.89 us |  82.877 us | 101.780 us |  0.44 |    0.01 |

以1024时的测验成果为例,来调查向量化算法比起标量算法的功能提高。

  • UseVectors:549.22/68.21 ≈ 8.05。
  • UseVectorsArgs:549.22/68.71 ≈7.99。

功能大幅度提高!这是由于 .NET 8.0 支撑了Avx512系列指令集,且这个CPU支撑。比照一下 Vectors.Instance右侧的信息,会发现现在多了 Avx512VL 指令集。在Avx512系列指令会集,Avx512VL便是负责处理128~256位数据的指令集。

其实,由于 .NET 8.0也优化了标量算法,这导致上面的的功能提高倍数看起来比较低。若拿 .NET 7.0的测验成果,与 .NET 8.0的UseVectors进行比照,就能看出差别了。

  • Scalar:1,120.3/68.21 ≈ 16.42。即 .NET 8.0向量算法的功能,是 .NET 7.0标量算法的 16.42 倍。
  • UseVectors:236.7/68.21 ≈ 3.47。即 .NET 8.0向量算法的功能,是 .NET 7.0向量算法的 3.47 倍。也可看做,Avx512的功能是Avx2的3.47倍。

相同是256位向量宽度,Avx512为什么能快这么多?这是由于Avx2没有供给“跨小道(lane)重排指令”,导致需求运用2条shuffle指令才干完成全256位的换位。而Avx512不只供给了“跨小道重排指令”(_mm_permutexvar_epi8),且供给了“2向量的跨小道重排指令”(_mm_permutex2var_epi8)。所以此刻Avx512的理论性是Avx2的4倍,上面的向量算法提高了 3.47 倍,与理论值匹配。

3.2 Arm 架构

相同的源代码能够在 Arm 架构上运转。

3.2.1 Arm 架构上.NET 6.0程序的测验成果。

Arm架构上.NET 6.0程序的基准测验成果如下。

Vectors.Instance:	VectorTraits128AdvSimdB64	// AdvSimd
YShuffleX3Kernel_AcceleratedTypes:	SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double

BenchmarkDotNet v0.14.0, macOS Sequoia 15.1.1 (24B91) [Darwin 24.1.0]
Apple M2, 1 CPU, 8 logical and 8 physical cores
.NET SDK 8.0.204
  [Host]     : .NET 6.0.33 (6.0.3324.36610), Arm64 RyuJIT AdvSIMD
  DefaultJob : .NET 6.0.33 (6.0.3324.36610), Arm64 RyuJIT AdvSIMD


| Method         | Width | Mean         | Error     | StdDev    | Ratio |
|--------------- |------ |-------------:|----------:|----------:|------:|
| Scalar         | 1024  |  1,504.84 us |  0.449 us |  0.375 us |  1.00 |
| UseVectors     | 1024  |    119.36 us |  0.042 us |  0.040 us |  0.08 |
| UseVectorsArgs | 1024  |     83.89 us |  0.160 us |  0.149 us |  0.06 |
|                |       |              |           |           |       |
| Scalar         | 2048  |  6,011.17 us |  1.346 us |  1.193 us |  1.00 |
| UseVectors     | 2048  |    476.02 us |  6.485 us |  6.066 us |  0.08 |
| UseVectorsArgs | 2048  |    328.52 us |  0.298 us |  0.264 us |  0.05 |
|                |       |              |           |           |       |
| Scalar         | 4096  | 24,403.68 us |  6.763 us |  6.326 us |  1.00 |
| UseVectors     | 4096  |  3,378.05 us |  1.674 us |  1.566 us |  0.14 |
| UseVectorsArgs | 4096  |  2,852.52 us | 22.086 us | 20.660 us |  0.12 |

以1024时的测验成果为例,来调查向量化算法比起标量算法的功能提高。

  • UseVectors:1,504.84/119.36 ≈ 12.61。
  • UseVec

扫描二维码推送至手机访问。

版权声明:本文由51Blog发布,如需转载请注明出处。

本文链接:https://www.51blog.vip/?id=683

分享给朋友:

“[C#] 对24位图画进行水平翻转(FlipX)的跨渠道SIMD硬件加速向量算法(运用YShuffleX3Kernel)” 的相关文章

Prime2_解法二:openssl解密凭证

Prime2_解法二:openssl解密凭证

Prime2_解法二:openssl解密凭证 本博客供给的一切信息仅供学习和研讨意图,旨在进步读者的网络安全意识和技能才能。请在合法合规的前提下运用本文中供给的任何技能、办法或东西。假如您挑选运用本博客中的任何信息进行非法活动,您将单独承当悉数法律责任。本博客清晰表明不支撑、不鼓舞也不参加任何方法的...

MacOS下用Homebrew装置yt-dlp并装备yt-dlp.conf

MacOS下用Homebrew装置yt-dlp并装备yt-dlp.conf

MacOS下用Homebrew装置yt-dlp视频下载器并装备.conf装备文件 装置yt-dlp brew install yt-dlp 检查是否装置成功 yt-dlp --version 2024.11.18 给yt-dlp增加装备文件 1、翻开访达文件夹,到~根目录下,通常是你的用户名文件...

万星开源项目:System Design Primer - 学习体系规划的必备攻略

万星开源项目:System Design Primer - 学习体系规划的必备攻略

GitHub 链接:https://github.com/donnemartin/system-design-primer 什么是 System Design Primer? System Design Primer 是一个专心于体系规划的开源项目,由 Donne Martin 创立并保护。它旨在...

RSA非对称加密算法中的密钥对生成与传输

RSA非对称加密算法中的密钥对生成与传输

PrimiHub一款由密码学专家团队打造的开源隐私核算途径,专心于共享数据安全、密码学、联邦学习、同态加密等隐私核算范畴的技能和内容。 RSA(Rivest–Shamir–Adleman)加密算法是一种根据大素数分化难题的非对称加密算法,由Ron Rivest、Adi Shamir和Leonard...

区块链资金盘,风险与机遇并存

区块链资金盘,风险与机遇并存

区块链资金盘是一种利用区块链技术进行非法集资和诈骗的行为。其运作模式和风险如下: 资金盘的运作模式1. 金字塔传销结构:资金盘通常采用金字塔式的传销结构,依靠新加入的会员资金来支付给早期会员的收益,从而形成“拆东墙补西墙”的格局。2. 高额回报诱惑:这些项目通常会承诺极高的回报率,吸引投资者投入资金...

开源系统linux,Linux系统的魅力与价值

开源系统linux,Linux系统的魅力与价值

Linux 是一个开源操作系统,它基于 Unix 操作系统,最初由芬兰的 Linus Torvalds 开发。Linux 是自由软件和开源软件发展中最著名的例子。与微软的 Windows 或苹果的 macOS 不同,Linux 是一个开源项目,这意味着它的源代码是公开的,任何人都可以自由地查看、修改...