注意:由于过去的经验和Profiler软件的建议,我正在优化.我意识到另一种优化方法是GetNeighbors
不经常调用,但这是目前的次要问题.
我有一个非常简单的功能如下所述.一般来说,我在foreach循环中调用它.我称这个功能很多(大约每秒100,000次).前段时间,我用Java编写了这个程序的一个变种,并且对我最终用4 if语句替换了几个for循环的速度感到反感.循环展开看起来很难看,但确实在应用程序速度上有明显的差异.所以,我想出了一些潜在的优化,并认为我会就他们的优点和建议征求意见:
使用四个if语句并完全忽略DRY原则.我相信这会根据过去的经验改善表现,但这让我感到难过.为了澄清,4个if语句将被粘贴到我经常调用getNeighbors()的任何地方,然后将foreach块的内部粘贴在其中.
以某种神秘的方式记住结果.
向所有正方形添加"neighbors"属性.在初始化时生成其内容.
使用代码生成实用程序将对GetNeighbors的调用转换为if语句作为编译的一部分.
public static IEnumerableGetNeighbors(Model m, Square s) { int x = s.X; int y = s.Y; if (x > 0) yield return m[x - 1, y]; if (y > 0) yield return m[x, y - 1]; if (x < m.Width - 1) yield return m[x + 1, y]; if (y < m.Height - 1) yield return m[x, y + 1]; yield break; } //The property of Model used to get elements. private Square[,] grid; //... public Square this[int x, int y] { get { return grid[x, y]; } }
注意:GetNeighbors函数花费的时间占用了m.get_Item的20%,另外80%用于方法本身.
布赖恩
我在代码中遇到了类似的事情.
我用C#找到的两件事对我帮助最大:
首先,不要害怕分配.C#内存分配非常非常快,因此动态分配数组通常比制作枚举器更快.但是,这是否有用将取决于您如何使用结果.我看到的唯一缺陷是,如果你返回一个固定大小的数组(4),你将不得不检查使用你的结果的例程中的边缘情况.
根据你的模型矩阵的大小,你可能最好先做一次检查,看看你是否处于边缘,如果没有,预先计算整个阵列并返回它.如果您处于边缘,则可以单独处理这些特殊情况(根据需要制作1或2个元素数组).这会在那里放一个更大的声明,但这在我的经验中往往更快.如果模型很大,我会避免预先计算所有邻居.Squares的开销可能超过收益.
根据我的经验,预分配和返回与使用产量使得JIT更有可能内联您的功能,这可以在速度上产生很大的不同.如果您可以利用IEnumerable结果,并且您并不总是使用每个返回的元素,那就更好了,但除此之外,预计算可能会更快.
另一件需要考虑的事情 - 我不知道在你的情况下在Square中保存了什么信息,但是如果hte对象相对较小,并且在大型矩阵中使用并且迭代很多次,请考虑将其作为结构.我有一个类似于此的例程(在循环中称为数十万或数百万次),并且在我的情况下将类更改为结构,加速了例程超过40%.这假设您正在使用.net 3.5sp1,因为JIT在最新版本中对结构进行了更多优化.
当然,转换到结构与类之间存在其他潜在的缺陷,但它可能会产生巨大的性能影响.