C#中使用stackalloc比普通变量慢吗?

3

我有两个实现uint128乘法的函数,采用了两种不同的方法:一个是使用变量,另一个是使用stackalloc“数组”。

变量版本

public static UInt128 operator *(UInt128 i, UInt128 j) {

 ulong I0 = i._uint0; ulong I1 = i._uint1; ulong I2 = i._uint2; ulong I3 = i._uint3;
 ulong J0 = j._uint0; ulong J1 = j._uint1; ulong J2 = j._uint2; ulong J3 = j._uint3;
 ulong R0 = 0; ulong R1 = 0; ulong R2 = 0; ulong R3 = 0;

 if (I0 != 0) {
   R0 += I0 * J0;
   R1 += I0 * J1;
   R2 += I0 * J2;
   R3 += I0 * J3;
 }
 if (I1 != 0) {
   R1 += I1 * J0;
   R2 += I1 * J1;
   R3 += I1 * J2;
 }
 if (I2 != 0) {
   R2 += I2 * J0;
   R3 += I2 * J1;
 }
 R3 += I3 * J0;

 R1 += R0 >> 32; R0 &= uint.MaxValue;
 R2 += R1 >> 32; R1 &= uint.MaxValue;
 R3 += R2 >> 32; R2 &= uint.MaxValue;
 R3 &= uint.MaxValue;

 return new UInt128((uint)R3, (uint)R2, (uint)R1, (uint)R0);
}

使用stackalloc关键字的版本

为了方便理解,[0 + 1][1 + 1]等操作留在代码中。实际上这些操作会被C#编译器优化为常量。

public unsafe static UInt128 operator *(UInt128 i, UInt128 j) {

  var I = stackalloc ulong[4];
  var J = stackalloc ulong[4];
  var R = stackalloc ulong[4];

  I[0] = i._uint0; I[1] = i._uint1; I[2] = i._uint2; I[3] = i._uint3;
  J[0] = j._uint0; J[1] = j._uint1; J[2] = j._uint2; J[3] = j._uint3;


  if (I[0] != 0) {
    R[0] += I[0] * J[0];
    R[0 + 1] += I[0] * J[1];
    R[0 + 2] += I[0] * J[2];
    R[0 + 3] += I[0] * J[3];
  }
  if (I[1] != 0) {
    R[1] += I[1] * J[0];
    R[1 + 1] += I[1] * J[1];
    R[1 + 2] += I[1] * J[2];
  }
  if (I[2] != 0) {
    R[2] += I[2] * J[0];
    R[2 + 1] += I[2] * J[1];
  }
  R[3] += I[3] * J[0];


  R[1] += R[0] >> 32; R[0] &= uint.MaxValue;
  R[2] += R[1] >> 32; R[1] &= uint.MaxValue;
  R[3] += R[2] >> 32; R[2] &= uint.MaxValue;
  R[3] &= uint.MaxValue;

  return new UInt128((uint)R[3], (uint)R[2], (uint)R[1], (uint)R[0]);
}

由于某些原因,“变量”版本似乎比“stackalloc”版本快约20%,无论是在x86还是x64上(使用C#7.2编译器,在.NET 4.6.1上进行了优化)。 尚未检查较新/较旧框架的性能,但怀疑它会类似,因此我的问题不仅适用于4.6.1,因为似乎通常情况下,stackalloc速度较慢。
是否有任何原因导致stackalloc版本速度较慢,考虑到两个版本分配完全相同数量的内存(12 * sizeof(ulong)),并以相同顺序执行完全相同的操作? 我真的希望通过stackalloc而不是变量来处理数组。

“variable” 版本根本不使用数组,这就是我猜测为什么它更快的原因。如果不是出于性能提升的原因,你为什么要使用 stackalloc - C.Evenhuis
那我最好停止评论了 :) - C.Evenhuis
@C.Evenhuis 感谢您关于“struct”的建议。我也考虑过,但是当我测试它时,它比stackalloc慢得多,即使我将其设置为C# 7.2的“ref struct”,这应该加快了速度,因为它仅存在于堆栈上。 - Fit Dev
这个内容有点老,但只是为了增加一些内容:你可以通过复制使用stackalloc的版本,删除“unsafe”关键字并将“stackalloc”改为“new”,来相当地测试“stackalloc”的优势。这将测试该函数的两个数组版本,一个使用堆栈分配,另一个从堆中分配。 - Iván Hernández
2
@Iván Hernández,真正的成本只有在GC启动时才会显现。我不知道从stackalloc返回的指针是否也是如此,但数组具有边界检查开销,如果编译器无法优化它,则确保您永远不会访问超出其长度的索引。 - TakeMeAsAGuest
显示剩余4条评论
1个回答

2

1
一个链接可能会提供一些解决这里提到的问题的方法,但请用自己的话概括要点。 - Procrastinator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接