繁体   English   中英

记录执行时间:计算机如何快速计算出算术?

[英]Recording time of execution: How do computers calculate arithemetic so fast?

一开始我的问题似乎很基本,但请耐心等待。

我编写了以下代码,以测试python从1计数到1,000,000需要多长时间。

import time

class StopWatch:
    def __init__(self, startTime = time.time()):
        self.__startTime = startTime
        self.__endTime = 0

    def getStartTime(self):
        return self.__startTime

    def getEndTime(self):
        return self.__endTime



    def stop(self):
        self.__endTime = time.time()



    def start(self):
        self.__startTime = time.time()

    def getElapsedTime(self):
        return self.__endTime - self.__startTime


count = 0
Timer = StopWatch()
for i in range(1, 1000001):
    count += i

Timer.stop()
total_Time = Timer.getElapsedTime()
print("Total time elapsed to count to 1,000,000: ",total_Time," milliseconds")

我计算出令人惊讶的短时间跨度。 是0.20280098915100098毫秒。 我首先要问: 这是正确的吗?

我希望执行时间至少为2或3毫秒,但我没想到它可以在不到半毫秒的时间内完成计算!

如果这是正确的,那将引出我的第二个问题: 为什么这么快?

我知道CPU本质上是为算术而构建的,但我仍然不希望它能够在十分之二毫秒的时间内达到百万。

就像@jonrsharpe所说的那样,也许您被时间计量单位欺骗了。

不过,第三代Intel i7能够达到120 + GIPS(即每秒数十亿次基本操作),因此,假设所有缓存命中且没有上下文切换(简单地说,没有意外等待),它可以很容易地从0到1G计数。说时间甚至更多。 可能不是使用Python,因为它有一些开销,但仍然可行。

解释现代CPU如何实现如此“疯狂”的速度是一个广泛的话题,实际上是不止一种技术的协作:

  1. 动态调度程序将重新排列基本指令,以尽可能减少冲突(因此,等待)
  2. 精心设计的缓存将立即提供代码和(尽管对于该基准测试而言,问题较少)。
  3. 动态分支预测器将分析代码并推测分支条件(例如“ for循环是否结束?”),以预测发生跳跃的可能性很高,并且“获胜”。
  4. 一个好的编译器将通过重新布置指令来减少冲突或使循环更快(通过展开,合并等)来提供额外的精力。
  5. 多精度算术可以利用MMX集合和类似集合提供的矢量运算。

简而言之,这些小奇迹如此昂贵是有原因的:)

首先,正如已经指出的,time()输出实际上以秒为单位,而不是毫秒。

另外,您实际上是对总共1m ** 2/2执行1m次加法,不算到1m,并且您正在初始化一个带有range一百万个长的列表(除非您在python 3上)。

我在笔记本电脑上进行了一个更简单的测试:

start = time.time()
i = 0;
while i < 1000000:
   i+=1
print time.time() - start

结果:

0.069179093451

因此,为70毫秒。 这意味着每秒可进行1400 万次操作。

让我们看一下Stefano可能引用的表( http://en.wikipedia.org/wiki/Instructions_per_second )并进行粗略估计。 他们没有像我一样的i5,但是最慢的i7足够接近。 它具有4个内核的80 GIPS时钟,每个内核20 GIPS。

(顺便说一句,如果您的问题是“如何使每个内核获得20 GIPS?”,这 无济于事 。这是 maaaagic 纳米技术)

因此,该内核每秒能够进行200 亿次操作,而我们得到的1400 万次差异仅为1400倍。

此时,正确的问题不是“为什么这么快?”,不是“ 为什么这么慢? ”。 可能是python开销。 如果我们在C中尝试该怎么办?

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

int i = 0;
int million = 1000000;
int main() {

    clock_t cstart = clock();
    while (i < million) {
     i += 1;
    }

    clock_t cend = clock();
    printf ("%.3f cpu sec\n", ((double)cend - (double)cstart) / CLOCKS_PER_SEC);
    return 0;
}

结果:

0.003 cpu sec

它比python快23倍,与每秒理论“基本操作”的数量仅相差60倍。 我在这里看到两个操作-比较和加法,所以相差30倍。 这是完全合理的,因为基本操作可能比我们的加法和比较小得多(让汇编专家告诉我们),而且我们也没有考虑上下文切换,缓存未命中,时间计算开销以及谁知道其他什么。

这也表明python执行相同操作的操作量是23倍。 这也是完全合理的,因为python是一种高级语言。 这是高级语言中的一种惩罚-现在您了解了为什么速度至关重要的部分通常用C编写。

另外,python的整数是不可变的,应该为每个新的整数分配内存(python运行时对此很聪明,不过)。

我希望能回答您的问题,并教给您一些有关如何执行令人难以置信的粗略估计=)

简短答案:正如jonrsharpe在评论中提到的,这是秒,而不是毫秒。

也正如Stefano所说,xxxxxx->检查他发布的答案。 除了ALU之外,它还有很多细节。

我只是想提一提-当您在类或函数中设置默认值时,请确保使用简单的不可变而不是进行函数调用或类似的操作。 您的课程实际上是在为所有实例设置计时器的开始时间-如果创建新的Timer,您会感到非常惊讶,因为它将使用以前的值作为初始值。 尝试此操作,第二个Timer不会重置Timer

#...
count = 0
Timer = StopWatch()
time.sleep(1)
Timer - StopWatch()
for i in range(1, 1000001):
    count += i
Timer.stop()
total_Time = Timer.getElapsedTime()
print("Total time elapsed to count to 1,000,000: ",total_Time," milliseconds")

您将得到大约1秒的时间,而不是您期望的时间。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM