C 将1-100整数乘以-1或将所述整数设置为零需要更多时间吗?
如果语言重要的话,这是针对C的。如果使用汇编语言,它会使用2的补码将事情设置为负数。对于这个变量,您将值“0”存储在变量int中。我不完全确定会发生什么 我得到:1.90s用户0.01s系统99%cpu 1.928下面的代码,我猜大部分运行时都是在添加计数器变量C 将1-100整数乘以-1或将所述整数设置为零需要更多时间吗?,c,performance,assembly,x86,C,Performance,Assembly,X86,如果语言重要的话,这是针对C的。如果使用汇编语言,它会使用2的补码将事情设置为负数。对于这个变量,您将值“0”存储在变量int中。我不完全确定会发生什么 我得到:1.90s用户0.01s系统99%cpu 1.928下面的代码,我猜大部分运行时都是在添加计数器变量 int i; int n; i = 0; while (i < 999999999) { n = 0; i++; n++; } inti; int n; i=0; 而(i
int i;
int n;
i = 0;
while (i < 999999999)
{
n = 0;
i++;
n++;
}
inti;
int n;
i=0;
而(i<99999999)
{
n=0;
i++;
n++;
}
我得到:4.56s用户0.02s系统99%cpu 4.613以下代码的总数
int i;
int n;
i = 0;
n = 5;
while (i < 999999999)
{
n *= -1;
i++;
n++;
}
return (0);
inti;
int n;
i=0;
n=5;
而(i<99999999)
{
n*=-1;
i++;
n++;
}
返回(0);
我对汇编并不是特别了解,但使用二者的补码运算比将一件事设置为另一件事花费更多的时间似乎并不直观。什么是使一个比另一个更快的底层实现,以及表面下发生了什么?或者我的测试只是一个糟糕的测试,不能准确地描述它在实践中的实际速度
如果它看起来毫无意义,原因是我可以通过简单地将地图上的一个整数乘以-1来实现一个“检查表”,这意味着它已经被检查过了(但我需要保留该值,所以当我进行检查时,我可以将它与-1进行比较)。但是我想知道这是否太慢了,我可以制作一个单独的布尔2D数组来检查值是否被检查,或者将我的数据结构更改为一个结构数组,这样它就可以保存int 1/0。我想知道最好的实现是什么——将-1操作本身执行10亿次,总计将达到5秒左右,不算我程序的其余部分。但是制作一个单独的10亿平方整数数组或创建一个10亿平方结构似乎也不是最好的方法 关于代码本身
我使用GCC 7.2将您的代码片段编译成x86_64程序集输出,无需任何优化。我还缩短了每段代码,而不更改程序集输出。以下是结果
代码1:
// C
int main() {
int n;
for (int i = 0; i < 999999999; i++) {
n = 0;
n++;
}
}
// assembly
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0
jmp .L2
.L3:
mov DWORD PTR [rbp-8], 0
add DWORD PTR [rbp-8], 1
add DWORD PTR [rbp-4], 1
.L2:
cmp DWORD PTR [rbp-4], 999999998
jle .L3
mov eax, 0
pop rbp
ret
// C
int main() {
int n = 5;
for (int i = 0; i < 999999999; i++) {
n *= -1;
n++;
}
}
// assembly
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 5
mov DWORD PTR [rbp-8], 0
jmp .L2
.L3:
neg DWORD PTR [rbp-4]
add DWORD PTR [rbp-4], 1
add DWORD PTR [rbp-8], 1
.L2:
cmp DWORD PTR [rbp-8], 999999998
jle .L3
mov eax, 0
pop rbp
ret
/C
int main(){
int n;
对于(int i=0;i<9999999;i++){
n=0;
n++;
}
}
//装配
主要内容:
推动rbp
mov rbp,rsp
莫夫·德沃德PTR[rbp-4],0
jmp.L2
.L3:
莫夫·德沃德PTR[rbp-8],0
添加DWORD PTR[rbp-8],1
添加DWORD PTR[rbp-4],1
.L2:
cmp DWORD PTR[rbp-4],999998
jle.L3
mov-eax,0
流行限制性商业惯例
ret
代码2:
// C
int main() {
int n;
for (int i = 0; i < 999999999; i++) {
n = 0;
n++;
}
}
// assembly
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0
jmp .L2
.L3:
mov DWORD PTR [rbp-8], 0
add DWORD PTR [rbp-8], 1
add DWORD PTR [rbp-4], 1
.L2:
cmp DWORD PTR [rbp-4], 999999998
jle .L3
mov eax, 0
pop rbp
ret
// C
int main() {
int n = 5;
for (int i = 0; i < 999999999; i++) {
n *= -1;
n++;
}
}
// assembly
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 5
mov DWORD PTR [rbp-8], 0
jmp .L2
.L3:
neg DWORD PTR [rbp-4]
add DWORD PTR [rbp-4], 1
add DWORD PTR [rbp-8], 1
.L2:
cmp DWORD PTR [rbp-8], 999999998
jle .L3
mov eax, 0
pop rbp
ret
/C
int main(){
int n=5;
对于(int i=0;i<9999999;i++){
n*=-1;
n++;
}
}
//装配
主要内容:
推动rbp
mov rbp,rsp
mov DWORD PTR[rbp-4],5
莫夫·德沃德PTR[rbp-8],0
jmp.L2
.L3:
neg德沃德私人有限公司[rbp-4]
添加DWORD PTR[rbp-4],1
添加DWORD PTR[rbp-8],1
.L2:
cmp DWORD PTR[rbp-8],999998
jle.L3
mov-eax,0
流行限制性商业惯例
ret
循环中的C指令在程序集中位于两个标签之间(.L3:
和.L2:
)。在这两种情况下,这是三条指令,其中只有第一条指令不同。在第一个代码中,它是mov
,对应于n=0代码>。然而,在第二代码中,它是neg
,对应于n*=-1代码>
根据,这两条指令的执行速度因CPU而异。在一个芯片上,一个比另一个快,而在另一个芯片上则慢。
感谢aschepler在评论中的输入
这意味着,所有其他指令都是相同的,一般来说,您无法判断哪些代码更快。因此,试图比较他们的表现是毫无意义的
关于你的意图
您询问这些短代码的性能的原因是错误的。你想要的是实现一个清单结构,而你对如何构建它有两个相互矛盾的想法。我们使用一个特殊的值,-1
,为映射中的变量添加特殊的含义。另一个使用额外的数据(外部布尔数组或每个变量的布尔值)来添加相同的含义,而不改变现有变量的用途
int i;
int n;
i = 0;
while (i < 999999999)
{
n = 0;
i++;
n++;
}
您必须做出的选择应该是一个设计决策,而不是由不明确的性能问题驱动。就个人而言,每当我在特殊值或具有精确含义的附加数据之间面临这种选择时,我倾向于后一种选择。这主要是因为我不喜欢处理特殊的价值观,但这只是我的观点
我的建议是选择您可以更好地维护的解决方案,即您最熟悉且不会损害未来代码的解决方案,并在重要的时候询问性能,或者更确切地说,询问性能是否重要。分配零非常便宜
但是你的微基准很少告诉你应该为你的大阵列做些什么。内存带宽/缓存未命中/缓存占用空间的考虑因素将占主导地位,而您的microbench根本不测试这一点
与使用单独的位图相比,使用整数值的一位表示选中/未选中似乎是合理的。(拥有一个由0/1 32位整数组成的单独数组完全是愚蠢的,但是位图是值得考虑的,特别是如果你想快速搜索下一个未选中的条目或下一个选中的条目的话。现在还不清楚你在用这个做什么,所以我只会继续解释在你的微基准中观察到的性能。)
顺便说一句,像这样的问题是一个很好的例子,说明为什么像“为什么你不自己做基准测试”这样的评论会被误导:因为你必须非常详细地了解你正在测试的内容,才能编写一个有用的微基准
显然,您是在调试模式下编译的,例如,gcc
,使用默认的-O0
,这会溢出