Che*_*ron 36 c++ java operators premature-optimization micro-optimization
我听过一位老师放弃了这一次,从那以后一直困扰着我.假设我们要检查整数x是否大于或等于0.有两种方法可以检查:
if (x > -1){
//do stuff
}
Run Code Online (Sandbox Code Playgroud)
和
if (x >= 0){
//do stuff
}
Run Code Online (Sandbox Code Playgroud)
根据这个老师>会稍快一点>=.在这种情况下它是Java,但据他说,这也适用于C,c ++和其他语言.这句话有什么道理吗?
Gra*_*and 29
它非常依赖于底层架构,但任何差异都是微不足道的.
如果有的话,我希望(x >= 0)稍快一些,因为0在某些指令集(例如ARM)上免费提供.
当然,任何合理的编译器都会选择最佳实现,无论您的源代码是哪种变体.
Mic*_*urr 29
任何现实世界的意义都没有区别.
让我们看看各种编译器为各种目标生成的一些代码.
-O2适用于GCC,/Ox适用于MSVC,-Oh适用于IAR)使用以下模块:
void my_puts(char const* s);
void cmp_gt(int x)
{
if (x > -1) {
my_puts("non-negative");
}
else {
my_puts("negative");
}
}
void cmp_gte(int x)
{
if (x >= 0) {
my_puts("non-negative");
}
else {
my_puts("negative");
}
}
Run Code Online (Sandbox Code Playgroud)以下是他们为比较操作生成的内容:
针对ARM的MSVC 11:
// if (x > -1) {...
00000 |cmp_gt| PROC
00000 f1b0 3fff cmp r0,#0xFFFFFFFF
00004 dd05 ble |$LN2@cmp_gt|
// if (x >= 0) {...
00024 |cmp_gte| PROC
00024 2800 cmp r0,#0
00026 db05 blt |$LN2@cmp_gte|
Run Code Online (Sandbox Code Playgroud)
针对x64的MSVC 11:
// if (x > -1) {...
cmp_gt PROC
00000 83 f9 ff cmp ecx, -1
00003 48 8d 0d 00 00 // speculative load of argument to my_puts()
00 00 lea rcx, OFFSET FLAT:$SG1359
0000a 7f 07 jg SHORT $LN5@cmp_gt
// if (x >= 0) {...
cmp_gte PROC
00000 85 c9 test ecx, ecx
00002 48 8d 0d 00 00 // speculative load of argument to my_puts()
00 00 lea rcx, OFFSET FLAT:$SG1367
00009 79 07 jns SHORT $LN5@cmp_gte
Run Code Online (Sandbox Code Playgroud)
针对x86的MSVC 11:
// if (x > -1) {...
_cmp_gt PROC
00000 83 7c 24 04 ff cmp DWORD PTR _x$[esp-4], -1
00005 7e 0d jle SHORT $LN2@cmp_gt
// if (x >= 0) {...
_cmp_gte PROC
00000 83 7c 24 04 00 cmp DWORD PTR _x$[esp-4], 0
00005 7c 0d jl SHORT $LN2@cmp_gte
Run Code Online (Sandbox Code Playgroud)
GCC 4.6.1针对x64
// if (x > -1) {...
cmp_gt:
.seh_endprologue
test ecx, ecx
js .L2
// if (x >= 0) {...
cmp_gte:
.seh_endprologue
test ecx, ecx
js .L5
Run Code Online (Sandbox Code Playgroud)
GCC 4.6.1针对x86:
// if (x > -1) {...
_cmp_gt:
mov eax, DWORD PTR [esp+4]
test eax, eax
js L2
// if (x >= 0) {...
_cmp_gte:
mov edx, DWORD PTR [esp+4]
test edx, edx
js L5
Run Code Online (Sandbox Code Playgroud)
针对ARM的GCC 4.4.1:
// if (x > -1) {...
cmp_gt:
.fnstart
.LFB0:
cmp r0, #0
blt .L8
// if (x >= 0) {...
cmp_gte:
.fnstart
.LFB1:
cmp r0, #0
blt .L2
Run Code Online (Sandbox Code Playgroud)
针对ARM Cortex-M3的IAR 5.20:
// if (x > -1) {...
cmp_gt:
80B5 PUSH {R7,LR}
.... LDR.N R1,??DataTable1 ;; `?<Constant "non-negative">`
0028 CMP R0,#+0
01D4 BMI.N ??cmp_gt_0
// if (x >= 0) {...
cmp_gte:
80B5 PUSH {R7,LR}
.... LDR.N R1,??DataTable1 ;; `?<Constant "non-negative">`
0028 CMP R0,#+0
01D4 BMI.N ??cmp_gte_0
Run Code Online (Sandbox Code Playgroud)
如果你还和我在一起,那么评估(x > -1)和(x >= 0)显示之间的差异就是:
cmp r0,#0xFFFFFFFF用于(x > -1)vs cmp r0,#0for (x >= 0).第一条指令的操作码长两个字节.我想这可能会引入一些额外的时间,所以我们称之为优势(x >= 0)cmp ecx, -1用于(x > -1)vs test ecx, ecxfor (x >= 0).第一条指令的操作码长一个字节.我想这可能会引入一些额外的时间,所以我们称之为优势(x >= 0)请注意,GCC和IAR为两种比较生成了相同的机器代码(可能的例外是使用了哪个寄存器).因此,根据这项调查,看起来(x >= 0)"快"的可能性微乎其微.但是,最小的操作码字节编码可能具有的优点(我强调可能有)将完全被其他因素所掩盖.
如果您发现Java或C#的jitted输出有任何不同,我会感到惊讶.我怀疑即使对于像8位AVR这样的非常小的目标,你也会发现任何差异.
总之,不要担心这种微观优化.我认为我在这里写的内容已经花费了更多的时间,而不是在我生命中执行它们的所有CPU中累积的这些表达式的性能差异所花费的时间.如果您有能力衡量性能差异,请将您的努力应用于更重要的事情,例如研究亚原子粒子的行为.
das*_*ght 19
你的老师一直在读一些很旧的书.过去一些架构缺乏greater than or equal评估>所需机器周期少的指令>=,但这些平台现在很少见.我建议寻求可读性和使用>= 0.
Ara*_*yan 13
这里更大的问题是过早优化.许多人认为写可读代码比写更重要的高效的码[ 1,2 ].一旦设计被证明有效,我会将这些优化应用为低级库中的最后一个阶段.
您不应该一直考虑以可读性为代价在代码中进行微小的优化,因为它会使读取和维护代码变得更加困难.如果需要进行这些优化,请将它们抽象为较低级别的函数,这样您仍然可以使用更容易为人类阅读的代码.
作为一个疯狂的例子,考虑一下将他们的程序编写成一个愿意放弃额外效率并使用Java在设计,易用性和可维护性方面获益的人.
作为旁注,如果您正在使用C,或许编写一个使用稍微更高效的代码的宏是一种更可行的解决方案,因为它将比分散操作更高效地实现效率,可读性和可维护性.
当然,效率和可读性的权衡取决于您的应用.如果该循环每秒运行10000次,那么它可能是瓶颈,你可能想花时间优化它,但如果它是一个单独的声明,偶尔会调用它可能不值得它获得微小的收益.
是的,有区别,你应该看到字节码.
对于
if (x >= 0) {
}
Run Code Online (Sandbox Code Playgroud)
字节码是
ILOAD 1
IFLT L1
Run Code Online (Sandbox Code Playgroud)
对于
if (x > -1) {
}
Run Code Online (Sandbox Code Playgroud)
字节码是
ILOAD 1
ICONST_M1
IF_ICMPLE L3
Run Code Online (Sandbox Code Playgroud)
版本1更快,因为它使用特殊的零操作数操作
iflt : jump if less than zero
Run Code Online (Sandbox Code Playgroud)
但是有可能看到仅在仅解释模式下运行JVM的差异java -Xint ...,例如此测试
int n = 0;
for (;;) {
long t0 = System.currentTimeMillis();
int j = 0;
for (int i = 100000000; i >= n; i--) {
j++;
}
System.out.println(System.currentTimeMillis() - t0);
}
Run Code Online (Sandbox Code Playgroud)
对于n = 0显示690 ms,对于n = 1显示760 ms.(我使用1代替-1,因为它更容易演示,这个想法保持不变)
| 归档时间: |
|
| 查看次数: |
2123 次 |
| 最近记录: |