【问题标题】:Using C# System..Numerics.Vector<T> to unpack / pack bits使用 C# System..Numerics.Vector<T> 解包/打包位
【发布时间】:2018-10-29 15:51:22
【问题描述】:

我正在测试 .Net C# System.Numerics.Vector 类用于打包和解包位的功能。

我希望 Vector 按位左移/右移功能,但目前不可用,因此我尝试使用如下算术和逻辑方法模拟移位。这是我看到的:

使用 Vector.Multiply() 和 Vector.BitwiseOr() 打包(模拟的按位 SHIFT LEFT 和 OR)比数组/指针代码稍差*。

*

但是使用 Vector.Divide() 和 Vector.BitwiseAnd() 解包(模拟按位 SHIFT RIGHT 和 AND)比数组/指针代码差得多**。

**50% 的吞吐量下降

注意:

  • 使用单元测试向量(这也在 cmets 中提出)。

  • 测试基础是在 65536 个整数的块中打包和解包 100Mn 到 1Bn 个整数。我为每个块随机生成了 int[]。

  • 我还测试了按位 (& | >>

  • 我将我的原始测试代码(用于非向量比较)更改为不安全/指针例程,以便在打包(一个单词中的多个整数)与解包方面创建更多类似的测试(一个词对许多整数)。这使非向量代码的整体差异(打包和解包之间)降低到

  • 非优化向量:打包速度是解包速度的 2 倍

  • 优化向量:在打包方面产生了 4 倍的改进(与非优化向量相比),在解包方面得到了 2 倍的改进

  • 未优化的数组/指针:解包比打包快约 5%

  • 优化的数组/指针:对打包产生了 3 倍的改进(相对于未优化的数组指针),对解包产生了 2.5 倍的改进。总体而言,优化的数组/指针打包比优化的数组/指针解包快

  • 优化的数组/指针打包比优化的向量包快约 10%

目前的结论:

  • Vector.Divide() 与普通算术除法相比似乎是一个相对较慢的实现

  • 此外,编译器似乎没有将 Vector.Divide() 代码优化到与 Vector.Multiply() 几乎相同的程度(它支持下面关于优化除法的 cmets)

    李>
  • 目前,数组/指针处理在打包数据方面比 Vector 类稍快,在解包方面明显更快

  • System.Numerics 需要 Vector.ShiftLeft() 和 Vector.ShiftRight() 方法

问题(更新);

  • 我的结论是否大致正确?还是有其他方面需要检查/考虑?

更多信息:

int numPages =  8192; // up to >15K     
int testSize = 65536;
StopWatch swPack = new StopWatch();
StopWatch swUnpack = new StopWatch();
long byteCount = 0;
for (int p = 0; p < numpages; b++)
{
    int[] data = GetRandomIntegers(testSize, 14600, 14800);

    swPack.Start();
    byte[] compressedBytes = pack(data);
    swPack.Stop();

    swUnpack.Start();
    int[] unpackedInts = unpack(compressedBytes);
    swUnpack.Stop();

    byteCount += (data.Length*4);

}
Console.WriteLine("Packing Throughput (MB/sec): " + byteCount / 1000 / swPack.ElapsedMilliseconds);
Console.WriteLine("Unpacking Throughput (MB/sec): " + byteCount / 1000 / swUnpacking.ElapsedMilliseconds);

【问题讨论】:

  • 与左移乘法不同,右移除法仅适用于 无符号 整数。如果您使用有符号整数,则编译器无法安全地使用移位优化除法。
  • @PaulR:你可以,只需要一些额外的指令来处理算术右移与有符号除法之间的舍入差异。请参阅 godbolt.org/g/6HDjhB 以获取来自 v4si div2(v4si v) { return v/2; } 的 gcc/clang 输出,其中 v4si 是 32 位 int 的 GNU C 本机向量。其他除数的代码生成很有趣; 2 的幂仍然很便宜,其他除数给你一个乘法逆元。
  • @PeterCordes:是的,我主要考虑 2 的幂,并直接转换为单个移位指令,但是当然还有进一步的优化来处理其他除数和带符号值的除法等,尽管这些好处并没有那么显着。
  • 我确实使用了 Vector。所以:我仔细检查了我的测试,然后想知道 AND、XOR、>> 和 > 与 的情况下测试了打包和解包,我看到了类似的差异,即解包速度要慢得多。如果我关闭“优化代码”,则打包:解包速度从 4 倍下降到 1.3 倍——这似乎表明 > 与 & 一起优化。这听起来对吗?
  • Division 据我所知,没有被 CLR 优化过。在尽可能优化的代码中,这仍然会生成对通用“除以某个向量”方法的函数调用 - 右手操作数是常量将被忽略。

标签: c# vector bitwise-operators simd system.numerics


【解决方案1】:

IL

/// non-SIMD fallback implementation for 128-bit right-shift (unsigned)
/// n: number of bit positions to right-shift a 16-byte memory image.
/// Vector(T) argument 'v' is passed by-ref and modified in-situ.
/// Layout order of the two 64-bit quads is little-endian.

.method public static void SHR(Vector_T<uint64>& v, int32 n) aggressiveinlining
{
    ldarg v
    dup
    dup
    ldc.i4.8
    add
    ldind.i8
    ldc.i4.s 64
    ldarg n
    sub
    shl

    ldarg v
    ldind.i8
    ldarg n
    shr.un

    or
    stind.i8

    ldc.i4.8
    add
    dup
    ldind.i8
    ldarg n
    shr.un
    stind.i8

    ret
}

伪代码

As<Vector<ulong>,ulong>(ref v) = (As<Vector<ulong>,ulong>(in v) >> n) | 
                                  (ByteOffsAs<Vector<ulong>,ulong>(in v, 8) << (64 - n));
ByteOffsAs<Vector<ulong>,ulong>(ref v, 8) >>= n;

C# 外部声明

static class vector_ext
{
    [MethodImpl(MethodImplOptions.ForwardRef | MethodImplOptions.AggressiveInlining)]
    extern public static void SHR(ref Vector<ulong> v, int n);
};

您可以将 IL (ildasm.exe) 和 C# (csc.exe) 生成的中间 .netmodule 二进制文件链接到一个使用link.exe 中的/LTCG(链接时代码生成)选项进行汇编。

运行时 x64 JIT 结果 (.NET Framework 4.7.2)

0x7FF878F5C7E0    48 89 4C 24 08       mov qword ptr [rsp+8],rcx
0x7FF878F5C7E5    8B C2                mov eax,edx
0x7FF878F5C7E7    F7 D8                neg eax
0x7FF878F5C7E9    8D 48 40             lea ecx,[rax+40h]
0x7FF878F5C7EC    48 8B 44 24 08       mov rax,qword ptr [rsp+8]
0x7FF878F5C7F1    4C 8B 40 08          mov r8,qword ptr [rax+8]
0x7FF878F5C7F5    49 D3 E0             shl r8,cl
0x7FF878F5C7F8    4C 8B 08             mov r9,qword ptr [rax]
0x7FF878F5C7FB    8B CA                mov ecx,edx
0x7FF878F5C7FD    49 D3 E9             shr r9,cl
0x7FF878F5C800    4D 0B C1             or  r8,r9
0x7FF878F5C803    4C 89 00             mov qword ptr [rax],r8
0x7FF878F5C806    48 83 C0 08          add rax,8
0x7FF878F5C80A    8B CA                mov ecx,edx
0x7FF878F5C80C    48 D3 28             shr qword ptr [rax],cl
0x7FF878F5C80F    C3                   ret

【讨论】:

    猜你喜欢
    • 2011-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多