0

我问这个问题是因为当使用重复的打印语句调试循环时,它会减慢程序的速度,这比我最初预期的要慢得多。我已经习惯了,但现在我很好奇为什么会出现这种情况的技术原因?在我看来,变量的各种计算和分配会比输出字符串更昂贵。

4

3 回答 3

6

不少,但另一个重要(甚至是最重要的)瓶颈与 CPU 无关:I/O 开销。一旦发送了字节码指令并将所有参数转换为字符串,就会调用一个函数将这些字符串写入sys.stdout. 根据您的系统和运行程序的方式,这可能是:

  1. 磁盘上的文件
  2. 来自终端仿真器的管道
  3. 一些捕获输出的 Python 对象(这就是 IDLE 所做的 IIRC)来做谁知道用它做什么(捕获它,将它放入 GUI 等)。

在第 1 种情况下,涉及磁盘 I/O,这很容易比写入 RAM 慢一个数量级。与今天的 CPU 相比,RAM 已经非常慢了。正如评论中所指出的,由于操作系统和 Python 的大量缓冲,这不是一个问题,但仍然需要时间来发出写入并且(取决于我不太了解的实现细节)它可能仍然需要一些时间如果有人过早地刷新任何缓冲区的时间。

在案例 #2 中,所有内容都保留在内存中,但它仍然是一个系统调用、一些复制,并且另一端必须读取它并用它做一些事情让你注意到(例如,在一个带有反别名字体,这本身就是一项复杂的任务)。问题不大,因为它可以同时发生,但它仍然会给 CPU 带来负载。

在第 3 种情况下,所有赌注都被取消。据我们所知,它可能会使用 bcrypt 对输出进行哈希处理并将其发送到月球。你碰巧使用IDLE吗?我记得有一个抱怨说 IDLE 在重定向输出时(是?)非常慢,尤其是在有很多尖齿的情况下。它必须捕获输出,将它与到目前为止的输出连接起来,然后让 Tkinter 渲染它。

于 2012-04-29T13:13:11.147 回答
2

一个巨大的,巨大的,巨大的数字,尤其是当输出在屏幕上可见时,例如在现代多任务系统的终端仿真器窗口中。

首先,如果您以十进制输出数字,则每个数字都有一个 divmod,与加法相比,这是一个相对昂贵的操作。(如果以十六进制输出,可能会便宜一些,因为每个数字都可以仅使用移位和掩码来提取。)如果输出浮点数,则涉及更多计算;对于日期和时间,需要考虑各种长度的月份、闰年、闰秒、DST 和时区。

但这只是计算和逻辑,因此与即将发生的事情相比相形见绌。

接下来 Python 必须将输出文本发送到终端进行显示,这意味着操作系统必须介入以通过缓冲区传输数据,然后唤醒其他进程。终端进程扫描其输入以查找控制序列以移动光标或更改颜色。然后文本渲染器扫描文本以查找需要特殊处理的字符:可能需要应用一些组合重音符号,或者需要重新排列一些从右到左的脚本以进行显示。

布局文本后,终端告诉窗口管理器需要重绘其窗口的哪个区域,然后窗口管理器检查它是否可见——它可能被最小化或隐藏在另一个窗口后面。终端被告知哪个区域实际需要绘制,最后以适当的字体和颜色、字距调整和抗锯齿绘制文本。窗口是否有看起来很酷的透明背景?这也必须合并。

根据窗口系统的不同,像素然后可以通过操作系统缓冲区再次到达合成管理器,合成管理器实际上将窗口内容绘制到屏幕上,同时考虑到窗口透明度。

最后,像素到达屏幕,在被数百万的继任者扫走之前,它们几乎没有时间被看到,因为你看到输出流过的速度太快而无法阅读。

令人惊讶的是,我们的计算机为我们做了多少工作。

于 2012-04-29T14:08:59.773 回答
1

这与 CPU 指令无关,至少与 Python 程序中的 CPU 指令无关。当您print使用终端模拟器(命令窗口)作为输出时,要打印的字符串被复制到内核缓冲区,然后复制到终端进程的内存中。开销在于上下文切换(两个进程都进行系统调用,即跳转到内核模式)和内存中字符串的复制。

于 2012-04-29T13:11:46.997 回答