我目前正在做一些涉及邻接矩阵的图形计算,而我正在优化它的每一点。
我认为可以优化的说明之一是标题中的一个,原始形式:
if ((adjMatrix[i][k] > 0) && (adjMatrix[k][j] > 0) && (adjMatrix[i][k] + adjMatrix[k][j] == w))
但为了方便起见,我会坚持使用标题中提供的表格:
if (a > 0 && b > 0 && a + b == c)
我不喜欢的是> 0部分(作为邻接矩阵,在它的初始形式中它只包含0和1,但随着程序的进展,零从2开始被替换为数字,直到没有更多的零。
我做了一个测试并删除了> a和b均为0,并且有显着改善。在60088次迭代中,有一个减少792ms ,从3672ms减少到2880ms,这是原始时间的78%,这对我来说是非常好的。
所以我的问题是:你能想到在C#中优化这样的语句并获得相同结果的某种方法吗?也许是一些按位操作或类似的东西,我对它们并不十分熟悉。
回答每一个想法,即使它不适合。我会自己做速度测试,让你知道结果。
编辑:这是一个编译器,我将在我的计算机上自己运行它。我刚刚描述的不是我抱怨的问题/瓶颈。它的当前形式的程序可以满足我的需求,但我只想推进它并使其尽可能基本和优化。希望这能澄清一点。
编辑我相信提供完整的代码是有用的,所以在这里,但请记住我在下面的粗体中所说的内容。 我想严格关注if语句。该程序基本上采用邻接矩阵并存储所有存在的路径组合。然后根据一些系数进行排序和修剪,但是我没有包括在内。
int w, i, j, li, k;
int[][] adjMatrix = Data.AdjacencyMatrix;
List<List<List<int[]>>> output = new List<List<List<int[]>>>(c);
for (w = 2; w <= 5; w++)
{
int[] plan;
for (i = 0; i < c; i++)
{
for (j = 0; j < c; j++)
{
if (j == i) continue;
if (adjMatrix[i][j] == 0)
{
for (k = 0; k < c; k++) // 11.7%
{
if (
adjMatrix[i][k] > 0 &&
adjMatrix[k][j] > 0 &&
adjMatrix[i][k] + adjMatrix[k][j] == w) // 26.4%
{
adjMatrix[i][j] = w;
foreach (int[] first in output[i][k])
foreach (int[] second in output[k][j]) // 33.9%
{
plan = new int[w - 1];
li = 0;
foreach (int l in first) plan[li++] = l;
plan[li++] = k;
foreach (int l in second) plan[li++] = l;
output[i][j].Add(plan);
}
}
}
// Here the sorting and trimming occurs, but for the sake of
// discussion, this is only a simple IEnumerable<T>.Take()
if (adjMatrix[i][j] == w)
output[i][j] = output[i][j].Take(10).ToList();
}
}
}
}
使用优化构建的分析器结果添加了评论。
顺便说一句,时间结果是通过这段代码获得的(没有排序和修剪,这大大增加了执行时间)。我的测量中没有其他部分。在此代码之前有一个Stopwatch.StartNew(),紧接着是一个Console.WriteLine(EllapsedMilliseconds)。
如果您想了解大小,邻接矩阵有406行/列。所以基本上只有for-instructions组合执行许多迭代,所以我没有很多优化选项。速度目前不是问题,但我想确保它已经准备就绪。
为了排除“优化其他部分”的问题,本主题也有讨论的余地,但对于这个具体问题,我只想找到解决方案作为一个抽象的问题/概念。它可以帮助我和其他人理解C#编译器如何工作并处理if语句和比较,这是我的目标。
答案 0 :(得分:6)
对于签名变量 a 和 b ,您可以将a>0 && b>0
替换为(a-1)|(b-1) >= 0
。
同样,条件x == w
可以表示为(x - w)|(w - x) >= 0
,因为当x != w
表达式的左侧或右侧部分将切换符号位时,该位由位保留明智的或。放在一起的所有内容都将(a-1)|(b-1)|(a+b-w)|(w-a-b) >= 0
表示为单一比较。
或者,轻微的速度优势可能来自推杆 增加顺序的可能性:
哪个更有可能(a|b)>=0
或(a+b)==w
?
答案 1 :(得分:4)
我不知道C#如何优化这样的事情,但尝试将adjMatrix[i][k]
和adjMatrix[k][j]
存储在临时变量中并不是很难读取内存两次。看看这是否以任何方式改变了事情。
很难相信算术和比较操作是这里的瓶颈。最有可能的是内存访问或分支。理想情况下,应以线性方式访问内存。你能做些什么来使它变得更线性吗?
很高兴看到更多代码可以提出更具体的建议。
更新:您可以尝试使用二维数组(int[,]
)而不是锯齿状数组(int[][]
)。这可能会提高内存局部性和元素访问速度。
答案 2 :(得分:3)
逻辑测试的顺序可能很重要(如其他答案中所述)。由于您使用的是短路逻辑测试(&amp;&amp;&amp;&amp;),因此从左到右评估条件,并且发现第一个条件为假,将导致程序停止评估条件和继续执行(不执行if
块)。因此,如果有一个条件比其余条件更可能是false
,那么应该首先进行,下一个应该是下一个最有可能成为false
等的条件。
另一个很好的优化(我怀疑它实际上是什么让你的性能提升 - 而不是简单地删除一些条件)是将你从数组中提取的值分配给局部变量。
您正在使用adjMatrix[i][k]
两次(以及adjMatrix[k][j]
),这迫使计算机挖掘数组以获取值。相反,在if语句之前,每次都将它们设置为局部变量,然后对这些变量进行逻辑测试。
答案 3 :(得分:1)
我同意其他人的观点,他们认为这个简单的陈述不太可能是您的瓶颈,并在您决定优化此特定行之前建议进行分析。但是,作为一个理论实验,你可以做几件事:
零检查:检查a != 0 && b != 0
可能比a >= 0 && b >= 0
稍快一些。由于你的邻接矩阵是非负的,你可以安全地做到这一点。
重新排序:如果仅a + b == c
测试更快,请先尝试使用此测试,然后再单独测试a
和b
。我怀疑这会更快,因为添加和相等检查比零检查更昂贵,但它可能适用于您的特定情况。
避免双重索引:使用ILDASM或等效项查看生成的IL,以确保数组索引仅被解除引用一次,而不是两次。如果不是,请在检查前将它们放入局部变量中。
答案 4 :(得分:1)
除非您正在调用函数,否则不会优化条件。这是毫无意义。但是,如果你真的想要记住一些简单的事情
检查条件是否为零(或不是),如果设置了最高位(或不是),并且比较(==或!=)基本上是a-b并检查其是否为零(== 0 )或不(!= 0)。所以a是无符号的,则&gt; 0与!= 0相同。如果a是有符号的,则&lt; 0非常好(这使用最高位检查)并且优于&lt; = 0。但无论如何只知道这些规则可能有所帮助。
同时启动一个分析器,你会看到条件在001%的时间。如果有的话你应该问如何写一些不需要条件的东西。
答案 5 :(得分:0)
您是否考虑过颠倒逻辑?
if (a > 0 && b > 0 && a + b == c)
可以改写为:
if (a == 0 || b == 0 || a + b != c) continue;
因为如果任何语句都是假的,你不想在循环中做任何事情,那么尽量尝试中止(如果运行时那么聪明,我假设)。
最重的操作应该是最后一次,因为如果第一个语句为真,则不需要检查其他语句。我认为增加是最重要的部分,但是分析它可能会讲述一个不同的故事。
但是,我没有将这些情景描述为我自己,并且在这些微不足道的条件下,它甚至可能是一个缺点。看到你的发现会很有意思。