c# - C# 中带有 SIMD 的 2x2 矩阵向量积
问题描述
我正在做一些事情,我想每秒将相同的 2x2short
值矩阵与不同的二维short
值向量相乘很多次,在这种情况下,性能很重要。现在,我只是以天真的方式做它并写出矩阵乘法。我查阅了 C# 的 SIMD 功能,发现没有办法制作这种类型的 2x2 矩阵。所以我尝试Vector<T>
使用System.Numerics.Vectors
. 构造函数希望至少有 4 个元素进入向量。我可以解决它并使其与 4 维向量一起使用,但我想知道是否有一种方法可以更轻松地完成我想做的事情:将 2x2 矩阵与 2 维向量相乘成一个新的 2 维向量与 SIMD。
解决方案
使用System.Runtime.Intrinsics.X86
,Sse2.MultiplyAddAdjacent
可以用来做繁重的工作,用一些洗牌等来排列数据。例如:
struct Vec2
{
public short X, Y;
}
struct Mat2x2
{
public short A, B, C, D;
}
static unsafe Vec2 Mul(Mat2x2 m, Vec2 v)
{
// movd: 0 0 0 0 0 0 Y X
var rawvec = Sse2.LoadScalarVector128((int*)&v);
// pshufd: Y X Y X Y X Y X
var vec = Sse2.Shuffle(rawvec, 0).AsInt16();
// movq: 0 0 0 0 D C B A
var mat = Sse2.LoadScalarVector128((ulong*)&m).AsInt16();
// pmaddwd: 0 0 DY+CX BY+AX
var dword_res = Sse2.MultiplyAddAdjacent(mat, vec);
// packssdw: 0 0 DY+CX BY+AX 0 0 DY+CX BY+AX
var rawres = Sse2.PackSignedSaturate(dword_res, dword_res);
Vec2 res;
*((int*)&res) = Sse2.ConvertToInt32(rawres.AsInt32());
return res;
}
生成的程序集相当合理:
mov dword ptr [rsp+10h],ecx
mov qword ptr [rsp+18h],rdx
vmovd xmm0,dword ptr [rsp+18h]
vpshufd xmm0,xmm0,0
vmovq xmm1,mmword ptr [rsp+10h]
vpmaddwd xmm0,xmm1,xmm0
vpackssdw xmm0,xmm0,xmm0
vmovd eax,xmm0
mov dword ptr [rsp],eax
mov eax,dword ptr [rsp]
但这并不理想。和函数参数(m
以及v
最后的结果)都是“反弹通过”内存......诚然,这正是 C# 代码所说的。这可以通过手动将X
and组合Y
成int
with 算术然后使用来解决ConvertScalarToVector128Int32
,但是 JIT 显然不够聪明,无法看到算术是多余的。所以似乎没有什么好的解决办法。希望在某个时候,JIT 优化器能够检测到这种毫无意义的“内存反弹”情况并将其删除。
另一点是MultiplyAddAdjacent
部分浪费了:它做了 8 个乘积,但只有 4 个是有用的计算,向量的上半部分只是零。如果您有 2 个向量要乘以相同的 2x2 矩阵,则可以以很小的额外成本来完成,比简单地调用上述函数两次要少得多。
推荐阅读
- c# - 如何将 Json 数组放入 Json 对象
- reactjs - 带有回调的 Nextjs 侦听器自行执行
- javascript - 如何从本地日期时间输入中减去时间?
- vba - 在 id 中抓取内部文本
- c# - 获取字段列表 - 动态 JSON
- azure - 没有重复时,如何解决 Azure 数据工厂“复制到”活动在映射错误中给出重复的列名
- json - 颤振错误:输入“列表”
' 不是类型 'String' 的子类型 - web-applications - SaaS 和 Web 应用程序有什么区别?
- html - 使用 html、css 和 bootstrap 实现分段按钮
- cucumber - Webdriver-IO 测试以时间成本 onPrepare 钩子开始