也见过类似的问题,但它们与缓存未命中或代码重新排序有关.在我的情况下,这些都不会发生.

代码非常简单:

#include <stdio.h>
#include <time.h>

void main()
{
    unsigned char a, b;
    int i;

    clock_t before = clock();
    for (i = 0; i < 10000000; i++) {
        a *= 2;
        b *= 2;
    }
    printf("elapsed: %lu\n", clock() - before);

    before = clock();
    for (i = 0; i < 10000000; i++) {
        a *= 2;
        a *= 2;
    }    
    printf("elapsed: %lu\n", clock() - before);
}

所以有两个变量ab,我用它们做一些事情(做什么并不重要). 第一个循环修改ab,第二个循环仅修改a.可以只有b就好了,没关系.

第二个循环大约需要两倍的时间.

a *= 2b *= 2的装配线如下所示:

    sal BYTE PTR -1[rbp]
    sal BYTE PTR -2[rbp]

对于第二个循环,它们看起来如下所示:

    sal BYTE PTR -2[rbp]
    sal BYTE PTR -2[rbp]

正如您所看到的,操作的数量是相同的,操作是相同的,内存访问是相同的,并且不可能发生缓存未命中.唯一的区别是在-1[rbp]-2[rbp]之间.

那么,为什么第二次循环需要两倍的时间呢?我只能猜测它在某种程度上与在两个连续操作中访问相同的变量有关.我可以想象,第一个操作需要一些时间来完成,第二个操作才能访问相同的var.而在第一个循环中,第一个操作是在一个变量上执行的,第二个操作是在另一个变量上执行的,因此它们是独立的,不必相互等待.但这应该意味着在CPU级别上存在一些微并行性.这是真的吗?

您也可以在以下位置在线运行该程序: https://onlinegdb.com/27qixKsWP

推荐答案

第一个循环可以是流水线的,因为这两个指令在不同的存储位置上操作.这两个乘法可以同时完成.

第二个循环在相同的内存位置上运行,因此第二个乘法必须等待第一个乘法完成.

C++相关问答推荐

C sscanf没有捕获第二个参数

为什么listen()(在调用accept()之前)足以让应用程序完成3次握手?

有效地计算由一组点构成的等边三角形和等腰三角形的数量

球体—立方体重叠:无、部分或全部?

需要大整数和浮点数.使用long long int和long double

在使用GTK 4 Columnview列表模型时,如何为多列添加排序函数.C编码,Linux/GNOME环境

在c++中使用堆栈的有效括号

Clang警告称,`ffi_type`与`sizeof`不兼容

struct -未知大小

限制不同类型的限定符

在移动数组元素时获得意外输出

如何在提取的索引中分配空值?

将数组插入数组

如何在双向表中实现线程安全,每个条目仅使用4位,同时避免任何全局锁?

如果格式字符串的内存与printf的一个参数共享,会发生什么情况?

使用C++中的字符串初始化 struct 时,从‘char*’初始化‘char’使指针变为整数,而不进行强制转换

Leet代码运行时错误:代码不会在Leet代码上编译,而是在其他编译器中编译,如netbeans和在线编译器

C 程序不显示任何输出,但它接受 CS50 Lab1 的输入问题

'printf("%s", user_input)' 危险吗?

#define X Defined(Y) 是有效的 C/C++ 宏定义吗?