【发布时间】:2019-08-07 08:23:48
【问题描述】:
在以下示例中,在 64 位架构上运行 32 位 ELF 更快,但我不明白为什么。我尝试了两个示例,一个使用除法,另一个使用乘法。表现符合预期,但该部门的表现令人惊讶。
我们在汇编中看到编译器正在调用 _alldiv,它在 32 位架构上模拟 64 位除法,因此它必须比简单地使用汇编指令 idiv 慢。所以我不明白我得到的结果:
我的设置是:Windows 10 x64、Visual Studio 2019
计时码我使用Measure-Command { .\out.exe }:
- 乘法
- 32 位 ELF:3360 毫秒
- 64 位 ELF:1469 毫秒
- 师
- 32 位 ELF:7383 毫秒
- 64 位 ELF:8567 毫秒
代码
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <Windows.h>
volatile int64_t m = 32;
volatile int64_t n = 12;
volatile int64_t result;
int main(void)
{
for (size_t i = 0; i < (1 << 30); i++)
{
# ifdef DIVISION
result = m / n;
# else
result = m * n;
# endif
m += 1;
n += 3;
}
}
64位反汇编(除法)
for (size_t i = 0; i < (1 << 30); i++)
00007FF60DA81000 mov r8d,40000000h
00007FF60DA81006 nop word ptr [rax+rax]
{
result = m / n;
00007FF60DA81010 mov rcx,qword ptr [n (07FF60DA83038h)]
00007FF60DA81017 mov rax,qword ptr [m (07FF60DA83040h)]
00007FF60DA8101E cqo
00007FF60DA81020 idiv rax,rcx
00007FF60DA81023 mov qword ptr [result (07FF60DA83648h)],rax
m += 1;
00007FF60DA8102A mov rax,qword ptr [m (07FF60DA83040h)]
00007FF60DA81031 inc rax
00007FF60DA81034 mov qword ptr [m (07FF60DA83040h)],rax
n += 3;
00007FF60DA8103B mov rax,qword ptr [n (07FF60DA83038h)]
00007FF60DA81042 add rax,3
00007FF60DA81046 mov qword ptr [n (07FF60DA83038h)],rax
00007FF60DA8104D sub r8,1
00007FF60DA81051 jne main+10h (07FF60DA81010h)
}
}
32位反汇编(除法)
for (size_t i = 0; i < (1 << 30); i++)
00A41002 mov edi,40000000h
00A41007 nop word ptr [eax+eax]
{
result = m / n;
00A41010 mov edx,dword ptr [n (0A43018h)]
00A41016 mov eax,dword ptr ds:[00A4301Ch]
00A4101B mov esi,dword ptr [m (0A43020h)]
00A41021 mov ecx,dword ptr ds:[0A43024h]
00A41027 push eax
00A41028 push edx
00A41029 push ecx
00A4102A push esi
00A4102B call _alldiv (0A41CD0h)
00A41030 mov dword ptr [result (0A433A0h)],eax
00A41035 mov dword ptr ds:[0A433A4h],edx
m += 1;
00A4103B mov eax,dword ptr [m (0A43020h)]
00A41040 mov ecx,dword ptr ds:[0A43024h]
00A41046 add eax,1
00A41049 mov dword ptr [m (0A43020h)],eax
00A4104E adc ecx,0
00A41051 mov dword ptr ds:[0A43024h],ecx
n += 3;
00A41057 mov eax,dword ptr [n (0A43018h)]
00A4105C mov ecx,dword ptr ds:[0A4301Ch]
00A41062 add eax,3
00A41065 mov dword ptr [n (0A43018h)],eax
00A4106A adc ecx,0
00A4106D mov dword ptr ds:[0A4301Ch],ecx
00A41073 sub edi,1
00A41076 jne main+10h (0A41010h)
}
}
编辑
为了进一步调查Chris Dodd,我稍微修改了我的代码如下:
volatile int64_t m = 32000000000;
volatile int64_t n = 12000000000;
volatile int64_t result;
这次我有这些结果:
- 师
- 32 位 ELF:22407 毫秒
- 64 位 ELF:17812 毫秒
【问题讨论】:
-
对 _alldiv 的转储感到好奇,看看它在做什么?可以是小整数的缓存表。 idiv 是 x86 上的已知猪,所以可能更快?
-
@MichaelDorgan 看来
__alldiv是:jbox.dk/sanos/source/lib/lldiv.asm.html -
MSVC 可以制作 ELF 可执行文件吗? WSL 甚至无法运行 32 位 Linux/ELF 可执行文件(仅限 x86-64),那么您是如何运行它的呢?另外,为什么要通过使输入变量
volatile以及递增它们来引入存储/重新加载内存依赖关系? -
@nowox: 有点 OT 注意:如果你还不知道,你应该看看 Agner Fog 的主页:agner.org/optimize。它包含很多关于这个主题的有价值的资源......
-
您为什么使用术语“ELF”?这是 Linux 和 Unix 上使用的可执行格式的名称。这只是 EXE / 可执行文件的错字吗?对于您可能正在制作的普通 Windows
.exe文件,Windows 使用 PE/COFF 可执行格式,而不是 ELF。
标签: c windows performance x86 division