Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我如何充分了解CLR,以便对性能问题做出有根据的猜测?_C#_.net_Performance_Optimization_Clr - Fatal编程技术网

C# 我如何充分了解CLR,以便对性能问题做出有根据的猜测?

C# 我如何充分了解CLR,以便对性能问题做出有根据的猜测?,c#,.net,performance,optimization,clr,C#,.net,Performance,Optimization,Clr,是的,我使用的是分析器(ANTS)。但在微观层面,它无法告诉你如何解决问题。我现在正处于微观优化阶段。例如,我分析了以下内容: for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { packedCells.Add(Data[x, y].HasCar); packedCells.Add(Data[x, y].RoadState); pack

是的,我使用的是分析器(ANTS)。但在微观层面,它无法告诉你如何解决问题。我现在正处于微观优化阶段。例如,我分析了以下内容:

for (int x = 0; x < Width; x++)
{
    for (int y = 0; y < Height; y++)
    {
        packedCells.Add(Data[x, y].HasCar);
        packedCells.Add(Data[x, y].RoadState);
        packedCells.Add(Data[x, y].Population);
    }
}
for(int x=0;x
ANTS显示y型环线需要花费大量时间。我认为这是因为它必须不断地称之为“高度获得者”。所以我创建了一个本地
intheight=height
在循环之前,并进行内循环检查,查看是否有
y
。这实际上使性能更差!蚂蚁现在告诉我x环线是个问题。嗯?这应该是无关紧要的,这是外环

最终我得到了一个启示——可能使用一个属性作为外部循环边界,使用一个本地属性作为内部循环边界,使得CLR经常在“本地”缓存和“这个指针”缓存之间跳转(我习惯于从CPU缓存的角度来思考)。所以我也做了一个局部的宽度,这就解决了它

从那以后,很明显,我也应该为数据创建一个local,即使数据甚至不是一个属性(它是一个字段)。事实上,这给我带来了更多的表现

然而,令人困惑的是,重新排列x和y循环(以提高缓存使用率)并没有带来任何区别,即使阵列很大(3000x3000)

现在,我想了解为什么我所做的事情提高了性能你建议我读什么书?

杰弗里·里克特(Jeffrey Richter)


这是一本很棒的书,有人在我的图书馆里偷走了它。

这里根本不涉及CLR,这本书应该被翻译成直接的机器代码,而不需要调用CLR。JIT编译器负责生成机器代码,它有一个优化器,试图产生最有效的代码。它有局限性,不能在上面花费大量时间

它所做的一件重要的事情是弄清楚哪些局部变量应该存储在CPU寄存器中。当你把Height属性放在一个局部变量中时,情况发生了变化。它可能决定将该变量存储在寄存器中。但现在,存储另一个变量的可用性减少了一个。就像x或y变量一样,对速度至关重要。是的,那会减慢速度

你对外环的诊断很差。这可能是由JIT优化器重新安排循环代码造成的,这使得分析器很难将机器代码映射回相应的C#语句

类似地,优化器可能会认为您使用数组效率低下,并将索引顺序切换回原处。不太确定它是否真的做到了,但也不是不可能

不管怎样,在这里你能获得一些洞察力的唯一方法就是查看生成的机器代码。关于x86汇编代码有很多不错的书,尽管现在可能有点难找到。您的起点是调试+Windows+反汇编

但是请记住,即使是机器代码也不能很好地预测代码的运行效率。现代CPU内核极其复杂,机器代码不再代表内核中实际发生的事情。唯一行之有效的方法就是你已经在做的事情:反复尝试


阿尔宾-不。老实说,我不认为在剖析器外运行会改变性能差异,所以我没有费心。你认为我应该这么做?你以前有过这样的问题吗?(不过,我在编译时对其进行了优化)

在调试器下运行会改变性能:在调试器下运行时,即时编译器会自动禁用优化(以使调试更容易)


如果必须,请使用调试器附加到已经运行的JITted进程。

Hm,我认为循环注册不是真正的问题。 1.我尽量避免每个内部循环访问数组
数据
三次。
2.我还建议,重新考虑三条
Add
语句:显然,您要访问一个集合三次,以添加一些无关紧要的数据。使每次迭代只进行一次访问,并添加包含三个条目的数据类型:

for (int y = 0; ... {
 tTemp = Data[x, y];
 packedCells.Add(new {
  tTemp.HasCar, tTemp.RoadState, tTemp.Population 
 });
}

从另一个角度来看,您基本上是通过将矩阵复制到数组(或其他顺序集合)来对矩阵进行矢量化。。。有必要吗?为什么不定义一个专门的索引器来模拟线性访问呢?更好的是,如果您只需要枚举条目(在该示例中,不需要随机访问),为什么不使用适当的LINQ表达式呢?

关于使用数组,您应该知道的一点是,CLR将始终确保数组索引不超出范围。它对一维数组进行了优化,但对2+维数组没有优化


知道了这一点,您可能希望基准测试
MyCell数据[][]
,而不是
MyCell数据[],]

,因为注释可能会被监督,我重复我自己的话:优化代码本身就很麻烦。您根本不需要显式地线性化矩阵,请参见上面的注释:定义一个实现
IEnumerable
的线性化适配器,并将其输入格式化程序

当我尝试添加另一个答案时,会收到警告,因此我将回收此答案:)在阅读了Steve的评论并思考了一会儿之后,我提出以下建议: 如果序列化多维数组的速度太慢(我还没有尝试过,我只是相信你…),就不要使用它!看起来,您的矩阵不是稀疏的,并且具有固定的维度。因此,将容纳单元格的结构定义为简单的线性阵列
[Serializable()]
class CellMatrix {
 Cell [] mCells;

 public int Rows { get; }
 public int Columns { get; }

 public Cell this (int i, int j) {
  get {
   return mCells[i + Rows * j];    
  }   
  // setter...
 }

 // constructor taking rows/cols...
}