Какой самый быстрый способ подготовить данные для Vector256 в c# - PullRequest
4 голосов
/ 22 апреля 2020

Я пытаюсь использовать 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>
    }
  }
}

То, что я уже пробовал:

  1. Чтобы построить пролеты из 4 парных чисел внутри l oop (очень медленно)

  2. Чтобы выделить 3 диапазона (слева, справа для вычитания и 3-го для результатов) за пределами l oop настолько большим, насколько позволяет стек, и заполнить их числами внутри l oop (довольно медленно)

  3. Чтобы использовать обычные массивы вместо Spans (видела ли какая-либо большая разница)

Я что-то упустил? Какой-то быстрый способ установить данные в памяти в правильном порядке, или в этом случае даже невозможно добиться лучшей производительности с SIMD?

PS Небольшое объяснение того, как я ожидал использовать 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);
      .....
    }
  }
}

PSS Кажется, что я не могу поместить поля 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 Ответ

0 голосов
/ 22 апреля 2020

У меня было бы три идеи для оптимизации вашего кода:

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 вместо Ваш для l oop (s). Это было бы возможно, если вы не используете результаты одной итерации в следующей итерации.

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;
    ...
...