C# 这两个代码段中的第一个在做更多工作时如何比第二个快3倍?
此代码如何:C# 这两个代码段中的第一个在做更多工作时如何比第二个快3倍?,c#,performance,x86,jit,x86-64,C#,Performance,X86,Jit,X86 64,此代码如何: var check = 0; for (var numerator = 0; numerator <= maxNumerator; numerator++) { check += numerator >= 0 ? numerator - (int) ((numerator * qdi.Multiplier) >> qdi.Shift) * qdi.Number : numerator - (int) -(
var check = 0;
for (var numerator = 0; numerator <= maxNumerator; numerator++)
{
check += numerator >= 0
? numerator - (int) ((numerator * qdi.Multiplier) >> qdi.Shift) * qdi.Number
: numerator - (int) -((-numerator * qdi.Multiplier) >> qdi.Shift) * qdi.Number;
}
return check;
var check = 0;
for (var numerator = 0; numerator <= maxNumerator; numerator++)
{
check += numerator >= 0
? (int) ((numerator * qdi.Multiplier) >> qdi.Shift)
: (int) -((-numerator * qdi.Multiplier) >> qdi.Shift);
}
return check;
var检查=0;
对于(变量分子=0;分子=0
分子-(int)((分子*量子点乘数)>>量子点移位)*量子点编号
:分子-(int)-(分子*qdi.Multiplier)>>qdi.Shift)*qdi.Number;
}
退货检查;
运行速度比此代码快3倍:
var check = 0;
for (var numerator = 0; numerator <= maxNumerator; numerator++)
{
check += numerator >= 0
? numerator - (int) ((numerator * qdi.Multiplier) >> qdi.Shift) * qdi.Number
: numerator - (int) -((-numerator * qdi.Multiplier) >> qdi.Shift) * qdi.Number;
}
return check;
var check = 0;
for (var numerator = 0; numerator <= maxNumerator; numerator++)
{
check += numerator >= 0
? (int) ((numerator * qdi.Multiplier) >> qdi.Shift)
: (int) -((-numerator * qdi.Multiplier) >> qdi.Shift);
}
return check;
var检查=0;
对于(变量分子=0;分子=0
?(int)((分子*量子点乘数)>>量子点移位)
:(int)-((-分子*qdi.乘数)>>qdi.Shift);
}
退货检查;
第一个代码段执行完全相同的快速除法操作(即先乘法,然后右移),但也执行减法和乘法,但JIT编译器似乎生成的代码较慢
我有每个可用的反汇编代码。较慢的代码推送rbx寄存器,并在开始时从rsp中减去10h,然后将其相加,并在结束时弹出rbx,而较快的代码则没有。
速度较慢的代码在大多数情况下也使用r11寄存器,而速度较快的代码使用rdx
有什么想法吗?三值运算中使用的条件可能会影响生成的代码 三元优化似乎比简单的if/else生成的代码效率更低 因此,将第二个snippest中的循环代码更改为:
if (numerator >= 0) check += (int) ((numerator * qdi.Multiplier) >> qdi.Shift);
else check += (int) -((-numerator * qdi.Multiplier) >> qdi.Shift);
或:
if(分子<0)check+=(int)-(分子*qdi.Multiplier)>>qdi.Shift);
else check+=(int)((分子*qdi.Multiplier)>>qdi.Shift);
或:
check+=分子<0
?(int)-((-分子*量子点乘数)>>量子点移位)
:(int)((分子*qdi.乘数)>>qdi.Shift);
将生成运行速度更快的代码
事实上,我觉得有点令人不安,四种组合中有三种会产生快速代码,而另一种组合会产生缓慢的代码……有时。你是如何衡量的?你对汇编语言的描述听起来并不像是会带来巨大性能差异的东西 无论如何,如果它真的慢了很多,我怀疑JIT团队之外的任何人都不能确切地说出发生这种情况的原因。我在进行.NET微基准测试时注意到,看似微不足道的代码更改可能会使代码运行得明显更快或更慢。如果您能够编写代码(这会触发缓慢)尽可能简单,您可以在Microsoft Connect上向MS投诉
您可以尝试复制qdi.Multiplier、qdi.Shift和qdi.Number(无论它们是什么)对于局部变量,这有时会有帮助。抓住救命稻草,但我想知道这是否与数据宽度有关;JIT在选择如何优化代码时确实考虑了数据的大小。如果使用不安全的块包装任一循环,会发生什么情况?请尝试以相反的顺序运行代码段。是否您遇到缓存问题未命中?我认为这对Raymond Chen来说是一个好问题!哦,另一个想法,它使用R11和RDX,因为它的目标是x64,我相信,如果你专门针对x86,会发生什么?Russ C:代码适用于任何CPU,但我尝试了x64和x86,结果相同;也尝试了不安全-仍然相同。不确定你所说的数据宽度是什么意思-都是int-ex除了乘法器,它是一个长的;在两种情况下都是相同的。史蒂文:已经分别作为单元测试和控制台应用程序运行了它们。始终是一样的。为什么不使用标准运算符进行除法?
QuickDivideInfo struct
的布局是什么?另外,您在不同的机器上看到了相同的行为吗?可以看到代码这里。这里只有一台机器,但很快将用测试代码更新文章,我们将看到!速度确实慢了很多。显示的循环被执行了50次,这是定时的。整个循环被循环了10次,以获得10个不同的计时。在两次传递之间运行一个完整的GC(包括等待待定的终结器)线程优先级设置为高于正常值,以尽可能减少中断。慢速运行时需要约10秒,快速运行时需要约3.2秒。这不是一个平凡的差别。:-)我同意局部变量可以稍微快一点,但这不是本例的重点。编译器应该在所有四种情况下生成相同的代码(?:with>=0;?:with<0;if/then with>=0和if/then with<0)