c# - 在 c# 中为 Vector256 准备数据的最快方法是什么?

标签 c# performance memory-management simd unsafe

我试图在我的函数中使用 SIMD 来提高性能,但似乎在内存中的数据准备过程中我丢失了更多。
我有什么:

public class SubProp
{
  public double subProp1, subProp2;
  <few functions>
}

public class MyClass
{
  public subProp prop1, prop2;
  <lots of other props>
}

public void DoSomeMath(List<MyClass> data)
{
  int cnt = data.Count;
  for(int i = 0; i < cnt; i++)
  {
    SubProp Prop1 = data[i].prop1;
    SubProp Prop2 = data[i].prop2
    for(int j = i + 1; j < cnt; j++)
    {
      SubProp Prop3 = data[j].prop1;
      SubProp Prop4 = data[j].prop2;          

      //these substructions suppose to be optimized with SIMD
      double res1 = Prop4.subProp2 - Prop3.subProp2;
      double res2 = Prop1.subProp1 - Prop2.subProp1;
      double res3 = Prop4.subProp1 - Prop3.subProp1;
      double res4 = Prop1.subProp2 - Prop2.subProp2;
      double res5 = Prop4.subProp1 - Prop2.subProp1;
      double res6 = Prop4.subProp2 - Prop2.subProp2;

      <some more math with res<n> variables and logical functions on results>
    }
  }
}

我已经尝试过的:
  • 在循环内构建 4 个 double 的跨度(非常慢)
  • 在堆栈允许的大小的循环外分配 3 个 Span(左、右用于减法,第三个用于结果),并在循环内用数字填充它们(非常慢)
  • 使用常规数组而不是跨度(确实看到了任何大的区别)

  • 我错过了什么吗?某种以正确顺序在内存中设置数据的快速方法,或者在这种情况下甚至不可能使用 SIMD 获得更好的性能?

    附言关于我如何在这里使用 Vector256 的一些解释:
    var lSide, rSide //prepared arrays or spans
    int vectorSize = 256 / 8 / 8;
    fixed (double* lptr = lSide)
    {
      fixed (double* rptr = rSide)
      {
        for (i = 0; i < array.Length - vectorSize; i += vectorSize) {
          var lVec = Avx2.LoadVector256(lptr);
          var rVec = Avx2.LoadVector256(rptr);
          resVector = Avx2.Subtract(lVec, rVec);
          .....
        }
      }
    }
    

    附注
    似乎我无法将类的 2 个字段的字段一一放入内存中:
    [StructLayout(LayoutKind.Explicit)]
    public sealed class SubProp
    {
        [FieldOffset(0)] public double subProp1;
        [FieldOffset(8)] public double subProp2;
        public double Sum() //just simple function for the test
        {
            return subProp1 + subProp2;
        }
    }
    [StructLayout(LayoutKind.Explicit)]
    public sealed class MyClass
    {
        [FieldOffset(0)] public SubProp prop1;
        [FieldOffset(16)] public SubProp prop2;
    }
    
    MyClass testObj = new MyClass
    {
      prop1 = new SubProp
      {
        subProp1 = 10,
        subProp2 = 20
      },
      prop2 = new SubPorp
      {
        subProp1 = 30,
        subProp2 = 40
      }
    };
    
    fixed(double* ptr = &testObj.prop1.subProp1)
    {
      for(int i=0; i<4; i=i+1)
      {
        Console.WriteLine(*(ptr+i));
    
      }
    }
    

    输出是:
    10
    20
    0
    <garbage>
    

    最佳答案

    我将有三个想法来优化您的代码:

    1.:以下两个操作:

      double res1 = data[i].prop2.subProp2 - data[j].prop1.subProp1;
      double res2 = data[j].prop1.subProp1 - data[i].prop2.subProp2;
    

    也可以写成:
      double res1 = data[i].prop2.subProp2 - data[j].prop1.subProp1;
      double res2 = res1 * -1; // or double res2 = -res1; Thanks to Peter Cordes for that hint...
    

    可能的情况:

    a = 5, b = 3:a - b = 2 和 b - a = -2

    a = 3,b = 0:a - b = 3 和 b - a = -3

    a = 0,b = 0:a - b = 0 和 b - a = 0

    a = 3, b = -2: a - b = 5 和 b - a = -5

    虽然我必须承认,我不知道那是否更快......

    2.:您可能会使用 Parallel.For 而不是您的 for 循环。如果您不在下一次迭代中使用一次迭代的结果,这将是可能的。

    3.:您多次访问您的属性。根据您执行此操作的频率,在本地存储它们可能会有好处。特别是那些通过 i 计数器访问的。
    for(int i = 0; i < cnt; i++)
    {
      SubProp dataIProp1 = data[i].prop1;
      SubProp dataIProp2 = data[i].prop2;
      for(int j = i + 1; j < cnt; j++)
      {
        //these substructions suppose to be optimized with SIMD
        double res1 = dataIProp2.subProp2 - data[j].prop1.subProp1;
        ...
    

    关于c# - 在 c# 中为 Vector256 准备数据的最快方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61363547/

    相关文章:

    c# - 具有相同 xaml 文件和不同 DataContext 的多个输入表单

    c# - 将 void 函数作为带有两个实参的参数传递

    c# - Paypal 支付和 .NET 确认/检查页面

    sql-server - SQL Server ODBC性能损失?

    库达;使用共享设备内存

    java - 内存分配是基于引用类还是实例类?

    c# - Razor Pages - 在所有 OnGet 处理程序之后从基类调用方法

    c++ - 缓存刷新例程之间的时间不一致

    sql-server - 有关具有庞大数据集的数据库结构的建议

    c - 使用清除内存违规中止而不是段错误