可托管代码影响指令级并行性

本文关键字:并行性 指令 影响 托管代码 | 更新日期: 2023-09-27 18:10:42

是否有任何方法可以影响指令级并行编写c#代码?换句话说,是否有一种方法可以"帮助"编译器生成最好地利用ILP的代码?我问这个问题是因为我试图从机器架构的一些概念中抽象出来,我需要知道这是否可能。如果没有,那么我将有理由从ILP中抽象出来。

EDIT:您将注意到我不想以任何方式使用c#来利用ILP。我的问题恰恰相反。转述:"我希望没有办法从c#中利用ILP"

谢谢。

可托管代码影响指令级并行性

ILP是CPU的一个特性。你没有办法控制它。编译器通过破坏依赖链来尽力利用它。

这可能包括。net JIT编译器,但是我没有证据证明这一点。

在获得指令级并行性时,您将受JIT的支配。谁知道JIT实际上做了哪些优化?如果我真的需要的话,我会选择另一种语言,比如c++。

为了最好地利用ILP,你需要打破依赖链。这应该仍然适用。

然而,在所有的抽象中,我怀疑在最极端的情况下仍然有可能有效地利用这一点。你有什么需要的例子吗?

没有明确或直接的方法来影响或提示。net编译器在IL或c#中执行此操作。这完全是编译器的工作。

你能做的唯一影响就是构建你的程序,使它更有可能(尽管不能保证)为你做这件事,而且很难知道它是否对结构起作用。

您可以在CLI中使用ILP。所以简短的回答是否定的。

稍微长一点:

我之前为一个简单的图像处理任务写了一段代码,并使用这种优化使我的代码快了"一点"。

一个简短的例子:

static void Main( string[] args )
{
  const int ITERATION_NUMBER = 100;
  TimeSpan[] normal = new TimeSpan[ITERATION_NUMBER];
  TimeSpan[] ilp = new TimeSpan[ITERATION_NUMBER];
  int SIZE = 4000000;
  float[] data = new float[SIZE];
  float safe = 0.0f;
  //Normal for
  Stopwatch sw = new Stopwatch();
  for (int iteration = 0; iteration < ITERATION_NUMBER; iteration++)
  {
    //Initialization
    for (int i = 0; i < data.Length; i++)
    {
      data[i] = 1.0f;
    }
    sw.Start();
    for (int index = 0; index < data.Length; index++)
    {
      data[index] /= 3.0f * data[index] > 2.0f / data[index] ? 2.0f / data[index] : 3.0f * data[index];
    }
    sw.Stop();
    normal[iteration] = sw.Elapsed;
    safe = data[0];
    //Initialization
    for (int i = 0; i < data.Length; i++)
    {
      data[i] = 1.0f;
    }
    sw.Reset();
    //ILP For
    sw.Start();
    float ac1, ac2, ac3, ac4;
    int length = data.Length / 4;
    for (int i = 0; i < length; i++)
    {
      int index0 = i << 2;
      int index1 = index0;
      int index2 = index0 + 1;
      int index3 = index0 + 2;
      int index4 = index0 + 3;
      ac1 = 3.0f * data[index1] > 2.0f / data[index1] ? 2.0f / data[index1] : 3.0f * data[index1];
      ac2 = 3.0f * data[index2] > 2.0f / data[index2] ? 2.0f / data[index2] : 3.0f * data[index2];
      ac3 = 3.0f * data[index3] > 2.0f / data[index3] ? 2.0f / data[index3] : 3.0f * data[index3];
      ac4 = 3.0f * data[index4] > 2.0f / data[index4] ? 2.0f / data[index4] : 3.0f * data[index4];
      data[index1] /= ac1;
      data[index2] /= ac2;
      data[index3] /= ac3;
      data[index4] /= ac4;
    }
    sw.Stop();
    ilp[iteration] = sw.Elapsed;
    sw.Reset();
  }
  Console.WriteLine(data.All(item => item == data[0]));
  Console.WriteLine(data[0] == safe);
  Console.WriteLine();
  double normalElapsed = normal.Max(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("Normal Max.: {0}", normalElapsed));
  double ilpElapsed = ilp.Max(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("ILP    Max.: {0}", ilpElapsed));
  Console.WriteLine();
  normalElapsed = normal.Average(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("Normal Avg.: {0}", normalElapsed));
  ilpElapsed = ilp.Average(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("ILP    Avg.: {0}", ilpElapsed));
  Console.WriteLine();
  normalElapsed = normal.Min(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("Normal Min.: {0}", normalElapsed));
  ilpElapsed = ilp.Min(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("ILP    Min.: {0}", ilpElapsed));
}

结果是(在。net framework 4.0客户端配置文件,发布):

在虚拟机上(我认为没有ILP):

真的真正的

和马克斯。: 111,1894
独立Max。: 106886

中国人均:78,163619
ILP平均值:77,682513

Nor Min.: 58,3035
ILP最小值:56,7672

On a Xenon:

真的真正的

和马克斯。[40,5892]
独立Max。: 30, 8906年

中国人均:35,637308
ILP平均值:25,45341


ILP最小值:23,7888

结果说明:

在调试中,编译器没有应用优化,但是第二个for循环比第一个更优化,所以有显著的区别。

答案似乎在发布模式构建程序集的执行结果中。IL编译器/JIT-er最好将性能消耗最小化(我认为甚至是ILP)。但是,无论您是否编写类似第二个for循环的代码,您都可以在特殊情况下获得更好的结果,并且在某些架构上,第二个循环可以优于第一个循环。但

你任由JIT摆布

遗憾的是,前面提到的

。遗憾的是,没有提到实现可以定义更多的优化,比如ILP(可以在规范中放置一个简短的段落)。但是它们不能列举代码的每一种架构优化形式,而CLI在更高的层次上:

这是从。net语言和IL中很好地抽象出来的。

这是一个非常复杂的问题,只有通过实验才能回答。我不认为这样能得到更精确的答案。我认为这个问题是误导性的,因为它不依赖于c#,它依赖于CLI的实现。

可能有很多影响因素,很难正确回答这样一个关于JIT的问题,直到我们把它想象成黑盒。

我在512-513页找到了关于循环矢量化和自动线程化的东西。http://www.ecma - international.org/publications/files/ecma st/ecma - 335. - pdf

我认为他们没有明确规定在这种情况下JIT-er需要如何表现,并且开发者可以选择优化的方式。所以我认为你可以影响,如果你可以编写更优化的代码,JIT将尝试使用ILP,如果它可能/实现。

我想因为他们没有说明,所以有可能。

所以答案似乎是否定的。我相信在CLI的情况下,如果规范没有这么说,你就不能从ILP中抽象出来。

:

我之前发现了一篇博客文章,但是直到现在我才找到它:http://igoro.com/archive/gallery-of-processor-cache-effects/例四包含了一个简短但恰当的回答。