当前位置: 首页 > 知识库问答 >
问题:

为什么一个CPU不能在一个简单的循环中实现与它的Ghz相当的FLOP性能?

甘永春
2023-03-14

我想知道为什么像这样的简单循环无法达到我的CPU时钟速度(4,2Ghz):

float sum = 0;    
for (int i = 0; i < 1000000; i+=1) {
    sum = sum * 1 + 1;
}

直观地说,我希望在不到1ms(如0238ms)的时间内实现这一点,每秒进行42亿次迭代。但我得到了大约3ms,即每秒大约3.33亿次迭代。

我假设做数学是两个周期,一个用于乘法,另一个用于求和。所以假设我在做6.66亿操作…看起来仍然很慢。然后我假设循环比较需要一个周期,循环计数器需要另一个周期…

所以我创建了以下代码来删除循环...

void listOfSums() {
    float internalSum = 0;
    internalSum = internalSum * 1 + 1;
    internalSum = internalSum * 1 + 1;
    internalSum = internalSum * 1 + 1;
    internalSum = internalSum * 1 + 1;
    // Repeated 100k times

令我惊讶的是,它变慢了,现在需要10毫秒。导致每秒只有1亿次迭代。

考虑到现代CPU使用流水线、无序执行、分支预测……我似乎无法通过在循环中执行两个浮点运算来饱和4,2Ghz的速度。

那么是否可以安全地假设4,2Ghz只能通过SIMD来实现,以使CPU内核完全充满任务,并且执行一个简单的循环将使您获得大约1/6的Ghz浮点性能?我尝试了不同的处理器,1/6似乎在大概范围内(英特尔、iPhone、iPad)。

瓶颈到底是什么?CPU解析指令的能力?SIMD只能超越哪一个?

共有1个答案

凌展
2023-03-14

通常,当前处理器可以在每个处理器周期中发出一个或多个浮点加法,并且可以在每个周期中发出一个或多个浮点乘法。浮点加法或乘法通常需要四个周期才能完成,这也是典型的。这意味着,一旦您开始了四个浮点加法(一个在周期 n 中,一个在周期 n 1 中,一个在周期 n 2 中,一个在周期 n 3 中),处理器可能在每个周期中完成一个加法 — 一个在周期 n 4 中(而一个新的也在周期 n 4 中开始),一个在周期 n 5 中, 等等。

然而,为了开始浮点运算,该运算的输入必须准备好。一旦< code>sum * 1在周期n开始,其结果可能要到周期n 4才准备好。因此< code>1的添加将在周期n 4中开始。并且该添加直到第8周期才会完成。然后使用该结果的下一次迭代中的乘法直到周期n 8才能开始。因此,利用所示的标称循环结构,每四个周期将完成一次浮点加法或乘法。

如果您尝试:

float sum0 = 0;
float sum1 = 0;
float sum2 = 0;
float sum3 = 0;    
for (int i = 0; i < 1000000; i += 1)
{
    sum0 = sum0 * 1 + 1;
    sum1 = sum1 * 1 + 1;
    sum2 = sum2 * 1 + 1;
    sum3 = sum3 * 1 + 1;
}

那么您可能会发现在同一时间内完成的浮点运算是原来的四倍。

这些细节因处理器模型而异。一些处理器可能在所有输入准备好之前就开始处理某些指令,一些处理器可能在结果传递到常规结果寄存器之前直接将结果早期转发到其他指令执行单元,等等,因此获得的性能在很大程度上取决于处理器特性。

以< code>listOfSums为例,代码大大超过了L1缓存的大小,因此处理器必须在执行指令之前从内存中加载每条指令,这大大降低了性能。

 类似资料:
  • 我目前有2个pthread正在运行,我想等待其中一个结束,以便我的程序继续运行。 在我的pthreads中,我有一个可以为真或假的变量(它是一个全局变量)。创建线程后(一个在cin中请求输入,一个等待10秒,如果达到10秒,它会杀死“cin”线程并结束自己,如果检测到cin,“cin”线程会杀死“计时器”线程),我希望我的程序等待。当每个线程结束时,它们将变量“stoptimer”置为真。 首先,

  • 问题内容: String s = “”; for(i=0;i<....){ s = some Assignment; } 要么 我不需要在循环外再次使用“ s”。第一个选项可能更好,因为不会每次都初始化一个新的String。但是,第二个结果将导致变量的范围仅限于循环本身。 编辑:回应米尔豪斯的回答。在循环中将String分配给常量是没有意义的吗?不,这里的“某些分配”是指从要迭代的列表中获得的变化

  • 问题内容: 我的意思是: 我用谷歌搜索,发现了这个: 表示定义接口方法的实现。但是接口没有实现,因此是不可能的。 但是,interface是100%抽象的类,抽象类可以实现接口(100%抽象的类)而无需实现其方法。将其定义为“接口”时会出现什么问题? 详细来说, 问题答案: 表示实现,当旨在声明仅提供不提供实现时。 A 100%,是功能上等同于,但它也可以实现,如果你想(在这种情况下,它不会保持1

  • 我有一个看起来很简单的问题,但由于某种原因我无法绕过它。基本上我的程序正在导致一个无限循环,我不知道为什么。 下面是我陷入的特定循环: 当我运行它时,它总是问我输入列#。我给它一个数字,它接受这个数字,$response变为True,但while循环继续运行,就好像<code>的$response</code>为false一样。我是Perl新手,所以可能我遗漏了一些东西,但是($response=

  • 类别:账户余额 我已经把这两个类都放在Balance.java和Account tBalance.java.这两个文件都在E:/程序/MyPack. Balance.java编译没有错误但是当我编译Account tBalance.java它给出错误:找不到符号"平衡". 我无法弄清楚为什么当两个类都在同一个包中声明时? 我正在使用javac B从MyPack编译alance.javajavac

  • 输入文件包含邻接列表,并具有以下格式的多行: 将类更改为静态后,这就是新的错误。 我觉得这是最麻烦的部分。 某些错误的屏幕快照