术语“CPU限制”和“I/O限制”是什么意思?


当前回答

CPU限制是指程序被CPU或中央处理器所限制,而I/O限制是指程序被I/O或输入/输出所限制,例如读写磁盘、网络等。

一般来说,在优化计算机程序时,人们试图找出瓶颈并消除它。知道您的程序受CPU限制是有帮助的,这样就不会不必要地优化其他东西。

[我所说的“瓶颈”是指使你的程序运行得比原本要慢的东西。]

其他回答

I/O限制是指完成计算所需的时间主要由等待输入/输出操作完成的时间决定的一种情况。

这与受CPU限制的任务相反。当请求数据的速度比消耗数据的速度慢时,或者换句话说,花费在请求数据上的时间比处理数据的时间多时,就会出现这种情况。

多线程是最重要的

在这个回答中,我将研究区分CPU和IO限制工作的一个重要用例:在编写多线程代码时。

RAM I/O绑定示例:Vector Sum

考虑一个程序,它将单个向量的所有值相加:

#define SIZE 1000000000
unsigned int is[SIZE];
unsigned int sum = 0;
size_t i = 0;
for (i = 0; i < SIZE; i++)
    /* Each one of those requires a RAM access! */
    sum += is[i]

通过为每个核心平均分割数组来实现并行,在普通的现代台式机上用处有限。

例如,在我的Ubuntu 19.04上,联想ThinkPad P51笔记本电脑,CPU:英特尔酷睿i7-7820HQ CPU(4核/ 8线程),RAM: 2倍三星M471A2K43BB1-CRC(2倍16GiB),我得到的结果如下:

图数据。

请注意,运行之间有很多差异。但是我不能进一步增加数组的大小,因为我已经是8GiB了,而且我今天没有心情进行多次运行的统计。然而,在做了许多手动运行之后,这似乎是一个典型的运行。

基准测试代码:

POSIX C线程源代码中使用的图形。 下面是一个c++版本,可以产生类似的结果。 剧情脚本

我不知道足够的计算机架构来完全解释曲线的形状,但有一件事是明确的:由于我使用了所有的8个线程,计算并没有像天真地期望的那样快8倍!出于某种原因,2和3个线程是最优的,增加更多线程只会让事情变得更慢。

将其与CPU约束的工作进行比较,后者实际上要快8倍:在time(1)的输出中,'real', 'user'和'sys'是什么意思?

所有处理器共享一个连接到RAM的内存总线的原因:

CPU 1   --\    Bus    +-----+
CPU 2   ---\__________| RAM |
...     ---/          +-----+
CPU N   --/

所以内存总线很快成为瓶颈,而不是CPU。

这是因为两个数字相加只需要一个CPU周期,而内存读取在2016年的硬件中大约需要100个CPU周期。

因此,CPU对每个字节的输入数据所做的工作太小了,我们称之为io绑定进程。

进一步加速计算的唯一方法是使用新的内存硬件加速单个内存访问,例如多通道内存。

例如,升级到更快的CPU时钟并不是很有用。

其他的例子

matrix multiplication is CPU-bound on RAM and GPUs. The input contains: 2 * N**2 numbers, but: N ** 3 multiplications are done, and that is enough for parallelization to be worth it for practical large N. This is why parallel CPU matrix multiplication libraries like the following exist: http://www.netlib.org/scalapack/pblas_qref.html http://icl.cs.utk.edu/magma/software/ Cache usage makes a big difference to the speed of implementations. See for example this didactic GPU comparison example. See also: Why can GPU do matrix multiplication faster than CPU? BLAS equivalent of a LAPACK function for GPUs Networking is the prototypical IO-bound example. Even when we send a single byte of data, it still takes a large time to reach it's destination. Parallelizing small network requests like HTTP requests can offer a huge performance gains. If the network is already at full capacity (e.g. downloading a torrent), parallelization can still increase improve the latency (e.g. you can load a web page "at the same time"). A dummy C++ CPU bound operation that takes one number and crunches it a lot: serial parallel Sorting appears to be CPU based on the following experiment: Are C++17 Parallel Algorithms implemented already? which showed a 4x performance improvement for parallel sort, but I would like to have a more theoretical confirmation as well The well known Coremark benchmark from EEMBC explicitly checks how well a suite of problems scale. Sample benchmark result clearing showing that: Workload Name (iter/s) (iter/s) Scaling ----------------------------------------------- ---------- ---------- ---------- cjpeg-rose7-preset 526.32 178.57 2.95 core 7.39 2.16 3.42 linear_alg-mid-100x100-sp 684.93 238.10 2.88 loops-all-mid-10k-sp 27.65 7.80 3.54 nnet_test 32.79 10.57 3.10 parser-125k 71.43 25.00 2.86 radix2-big-64k 2320.19 623.44 3.72 sha-test 555.56 227.27 2.44 zip-test 363.64 166.67 2.18 MARK RESULTS TABLE Mark Name MultiCore SingleCore Scaling ----------------------------------------------- ---------- ---------- ---------- CoreMark-PRO 18743.79 6306.76 2.97 the linking of a C++ program can be parallelized to a certain degree: Can gcc use multiple cores when linking?

如何发现如果你是CPU或IO限制

非ram IO绑定像磁盘,网络:ps aux,然后检查CPU% / 100 < n线程。如果是,你是IO绑定,例如阻塞读取只是等待数据和调度器跳过该过程。然后使用sudo iotop等进一步的工具来确定哪个IO是问题所在。

或者,如果执行速度很快,并且对线程数量进行了参数化,那么对于CPU限制的工作,您可以很容易地从时间上看到性能随着线程数量的增加而提高:在time(1)的输出中,'real'、'user'和'sys'是什么意思?

RAM- io绑定:更难判断,因为RAM等待时间包含在CPU%测量中,请参见:

如何检查应用程序是否cpu或内存限制? https://askubuntu.com/questions/1540/how-can-i-find-out-if-a-process-is-cpu-memory-or-disk-bound

一些选项:

Intel Advisor rooline(非免费):https://software.intel.com/en-us/articles/intel-advisor-roofline(存档)“屋顶线图是与硬件限制相关的应用程序性能的可视化表示,包括内存带宽和计算峰值。”

GPUs

当您第一次将输入数据从常规CPU可读RAM传输到GPU时,GPU会遇到IO瓶颈。

因此,对于CPU受限的应用,gpu只能比CPU更好。

然而,一旦数据传输到GPU,它可以比CPU更快地对这些字节进行操作,因为GPU:

有更多的数据本地化比大多数CPU系统,所以数据可以更快地访问某些核心比其他 利用数据并行性并牺牲延迟,跳过尚未准备好立即操作的任何数据。 由于GPU必须处理大量的并行输入数据,因此最好跳过可能可用的下一个数据,而不是像CPU那样等待当前数据可用并阻塞所有其他操作

因此GPU可以比CPU更快,如果你的应用程序:

可以高度并行化:不同的数据块可以在同一时间彼此分开处理 每个输入字节需要足够多的操作(不像向量加法,每个字节只做一次加法) 有大量的输入字节

这些设计选择最初是针对3D渲染的应用,其主要步骤如OpenGL中的什么是shaders,我们需要它们做什么?

顶点着色器:用4x4矩阵乘以一堆1x4向量 片段着色器:计算一个三角形的每个像素的颜色基于它在三角形内的相对位置

因此我们得出结论,这些应用程序是cpu限制的。

随着可编程GPGPU的出现,我们可以观察到几个GPGPU应用程序作为CPU绑定操作的例子:

Image Processing with GLSL shaders? Local image processing operations such as a blur filter are highly parallel in nature. Is it possible to build a heatmap from point data at 60 times per second? Plotting of heatmap graphs if the plotted function is complex enough. https://www.youtube.com/watch?v=fE0P6H8eK4I "Real-Time Fluid Dynamics: CPU vs GPU" by Jesús Martín Berlanga Solving partial differential equations such as the Navier Stokes equation of fluid dynamics: highly parallel in nature, because each point only interacts with their neighbour there tend to be enough operations per byte

参见:

为什么我们还在使用cpu而不是gpu ? gpu的弱点是什么? https://www.youtube.com/watch?v=_cyVDoyI6NE“CPU vs GPU(有什么区别?)-计算机爱好者”

CPython全局解释器锁

作为一个快速的案例研究,我想指出Python全局解释器锁(GIL): CPython中的全局解释器锁(GIL)是什么?

这个CPython实现细节可以防止多个Python线程有效地使用cpu限制的工作。CPython文档说:

CPython implementation detail: In CPython, due to the Global Interpreter Lock, only one thread can execute Python code at once (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better use of the computational resources of multi-core machines, you are advised to use multiprocessing or concurrent.futures.ProcessPoolExecutor. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously.

因此,这里有一个例子,cpu绑定的内容不适合,而I/O绑定的内容适合。

JavaScript async和Node.js worker_threads

这种情况与Python类似。

JavaScript基本上是单线程的。不确定这是否是语言标准的一部分(对于Python来说,它不是,除了参考CPython实现AFAIK之外,甚至没有一个语言标准)。

JavaScript有async关键字,它允许执行暂停,然后它开始执行其他东西。你可以这样写:

async function myfunc(init) {
  ret = init
  for (let i = 0; i < 1000000; i++) {
    ret += i*i + 2*i + 3
  }
  return ret;
}

async function doNetworkRequest(init) {
  // Some native method that gets network data.
}

const [first, second, myfunc_ret1, myfunc_ret2] = await Promise.all([
  doNetworkRequest('example.com'),
  doNetworkRequest('example2.com'),
  myfunc(1),
  myfunc(2),
])

await表示“等待所有这些异步的事情在继续之前完成”。

然而,在CPU上一次只能运行一个异步方法,因此myfunc的CPU密集型工作根本不会因此而加速。

然而,网络访问的原型IO绑定操作可以得到加速,因为这将一个接一个地触发两个网络请求,并在服务器/网络完成工作时等待两者返回。

事实上,该语言中有一个专门用于此的关键字async,这说明:在浏览器主导的JavaScript上下文中,网络请求非常重要。

然而,随着Node.js的出现,人们开始越来越想要并行化CPU工作负载,他们达成了与CPython类似的解决方案:创建单独的进程而不是线程。这是通过worker_threads库完成的,它:

实际上以JavaScript脚本路径作为输入,并启动一个新的解释器实例来运行它 由于进程不共享内存空间,因此可以使用消息传递系统在实例之间传递消息

https://medium.com/@Trott/ using-work-threads-in -node-js-80494136dbb6包含一个很好的例子。

worker_threads的文档再次说明了这个答案中其他地方提到的差异:

worker(线程)对于执行cpu密集型JavaScript操作非常有用。它们对I/ o密集型工作没有太大帮助。Node.js内置的异步I/O操作比Workers更高效。

在浏览器上也有Web Workers,不知道它与Node的实现有什么区别:Web Workers和Worker Threads之间有什么区别?

CPU限制是指程序被CPU或中央处理器所限制,而I/O限制是指程序被I/O或输入/输出所限制,例如读写磁盘、网络等。

一般来说,在优化计算机程序时,人们试图找出瓶颈并消除它。知道您的程序受CPU限制是有帮助的,这样就不会不必要地优化其他东西。

[我所说的“瓶颈”是指使你的程序运行得比原本要慢的东西。]

这很直观:

如果一个程序在CPU更快的情况下运行得更快,那么它就受到了CPU的限制,也就是说,它的大部分时间都在简单地使用CPU(进行计算)。计算π的新数字的程序通常是cpu限制的,它只是处理数字。

如果一个程序能够在I/O子系统更快的情况下运行得更快,那么它就是I/O约束的。具体的I/O系统是不同的;我通常把它与磁盘联系在一起,当然,一般来说,网络或通信也很常见。在一个大文件中查找一些数据的程序可能会成为I/O限制,因为瓶颈是从磁盘读取数据(实际上,这个例子在今天可能有点过时,从ssd读取数百MB/s)。

IO绑定进程:花更多的时间做IO比计算,有很多 短CPU突发。 CPU绑定进程:花费更多的时间进行计算,很少有很长时间的CPU爆发