术语"CPU绑定"和"I/O绑定"是什么意思?

Dev*_*per 276 optimization terminology

术语"CPU绑定"和"I/O绑定"是什么意思?

unw*_*ind 389

这非常直观:

如果CPU速度更快,则程序受CPU限制,即它大部分时间都花在CPU上(进行计算).计算π的新数字的程序通常是CPU绑定的,它只是处理数字.

如果I/O子系统更快,程序将受I/O限制.哪个确切的I/O系统可能会有所不同; 我通常将它与磁盘相关联,但当然网络或通信也很常见.一个程序通过一个巨大的文件查找某些数据可能会成为I/O绑定,因为瓶颈就是从磁盘读取数据(实际上,这个例子可能有点老式,现在数百MB/s从SSD进来).

  • I/O 是“输入/输出”。 (3认同)
  • “老式”不,不是,带宽不是延迟。 (2认同)

San*_*a R 221

CPU Bound意味着进程的进展速度受CPU速度的限制.对一小组数字执行计算的任务(例如乘以小矩阵)可能受CPU限制.

I/O Bound意味着进程进展的速率受I/O子系统速度的限制.处理来自磁盘的数据的任务(例如,计算文件中的行数)可能受I/O限制.

内存限制意味着进程进展的速率受可用内存量和内存访问速度的限制.处理大量内存数据的任务(例如乘以大矩阵)很可能是Memory Bound.

高速缓存限制是指进程进度受可用高速缓存的数量和速度限制的速率.简单地处理比缓存中的数据更多的数据的任务将是缓存绑定.

I/O Bound会慢于Memory Bound会慢于Cache Bound会比CPU Bound慢.

受I/O限制的解决方案不一定要获得更多内存.在某些情况下,可以围绕I/O,内存或缓存限制设计访问算法.请参阅Cache Oblivious Algorithms.


Cir*_*四事件 44

多线程是区分重要的情况,如下面的例子所述.

RAM I/O绑定示例:矢量和

考虑一个对单个向量的所有值求和的程序:

#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]
Run Code Online (Sandbox Code Playgroud)

通过为每个核心平均分割阵列来并行化,这对于常见的现代桌面来说是有限的.C++基准测试:https://github.com/cirosantilli/algorithm-cheat/blob/ea16f6bba12e7dcc32c0cbbbcdc74bcc2fd2d05b/src/cpp/interactive/sum_array_parallel.cpp

在GCC 5.2.1上测试,Ubuntu 15.10采用4核Intel i5-3210M,联想T430.示例典型结果(自多线程以来变量):

CPU 1   --\    Bus    +-----+
CPU 2   ---\__________| RAM |
...     ---/          +-----+
CPU N   --/
Run Code Online (Sandbox Code Playgroud)

4线程的计算速度不是预期的4倍!

所有处理器共享一条链接到RAM的内存总线的原因是:

2 * N**2
Run Code Online (Sandbox Code Playgroud)

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

这是因为两数相加取单个 CPU周期,存储器中读取需要大约100个CPU周期在2016年的硬件.

因此,每个输入数据字节完成的CPU工作量太小,我们将其称为IO绑定过程.

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

例如,升级到更快的CPU时钟并不是非常有用.

其他例子

  • 矩阵乘法在RAM和GPU上受CPU限制.输入包含:

    N ** 3
    
    Run Code Online (Sandbox Code Playgroud)

    数字,但是:

    #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]
    
    Run Code Online (Sandbox Code Playgroud)

    完成乘法运算,这足以使并行化对于实际的大N而言是值得的.

    这就是图书馆的原因:

    存在.

    缓存使用对实现的速度有很大影响.例如,参见这个教学GPU比较示例.

  • GPU在将数据传输到CPU时存在IO瓶颈.

    它们的设计使得渲染输出(像素矩形)可以直接输出到视频存储器,以避免CPU往返.

  • 网络是典型的IO绑定示例.

    即使我们发送一个字节的数据,它仍然需要很长时间才能到达目的地.

    并行化诸如HTTP请求之类的小型网络请求可以提供巨大的性能提升.

    如果网络已经满负荷运行(例如下载torrent),并行化仍然可以增加延迟(例如,您可以"同时"加载网页).

  • 一个虚拟的C++ CPU绑定操作,它接受一个数字并对其进行大量处理:

如何确定您是CPU还是IO绑定

非RAM IO绑定像磁盘,网络:pthread,然后确定如果ps aux.如果是,则是IO绑定,例如,阻塞CPU% / 100 < n threads只是在等待数据,并且调度程序正在跳过该进程.然后使用其他工具read来确定哪个IO完全是问题.

RAM-IO绑定:很难说,因为RAM等待时间包含在sudo iotop测量中.也许您可以做的最好的事情是估计缓存未命中.

也可以看看:

  • 哥们,你怎么能这么有能力,写出这样的文章呢? (3认同)
  • @MikayilAbdullayev 谢谢!我倾向于只回答“重要问题”,并且当我学习新的相关内容时,我倾向于一遍又一遍地回答这些问题。 (3认同)
  • @CiroSantilliOurBigBook.com ~向兄弟致敬! (2认同)

Chr*_*Rea 28

CPU绑定意味着程序受到CPU或中央处理单元的瓶颈,而I/O绑定意味着程序受到I/O或输入/输出的瓶颈,例如读取或写入磁盘,网络等.

通常,在优化计算机程序时,人们会试图找出瓶颈并消除它.知道你的程序是CPU绑定有帮助的,所以不会不必要地优化其他东西.

[并且通过"瓶颈",我的意思是让你的程序变得比它原本要慢的东西.]


gim*_*mel 17

另一种表达同一想法的方法:

  • 如果加速CPU不会加速程序,则可能是I/O限制.

  • 如果加速I/O(例如使用更快的磁盘)没有帮助,那么您的程序可能受CPU限制.

(我使用"可能是"因为你需要考虑其他资源.记忆就是一个例子.)


Pau*_*lin 9

当程序等待I/O(即磁盘读/写或网络读/写等)时,即使程序停止,CPU也可以自由执行其他任务.程序的速度主要取决于IO的发生速度,如果你想加快速度,你需要加快I/O.

如果你的程序运行了很多程序指令而不是等待I/O,那么它就被认为是CPU绑定的.加速CPU将使程序运行得更快.

在任何一种情况下,加速程序的关键可能不是加速硬件,而是优化程序以减少所需的IO或CPU数量,或者让I/O同时执行CPU密集型操作东西.


小智 7

IO 绑定进程:花在 IO 上的时间多于计算,有许多短 CPU 突发。CPU 绑定进程:花费更多时间进行计算,很少有很长的 CPU 突发


Fel*_*e84 5

I/O bound指的是一种条件,其中完成计算所花费的时间主要由等待输入/输出操作完成所花费的时间来确定.

这与CPU绑定的任务相反.当请求数据的速率低于其消耗速率时,或者换句话说,花费更多时间来请求数据而不是处理数据时,就会出现这种情况.


Taş*_*şah 5

看看微软怎么说。

异步编程的核心是任务和任务对象,它们对异步操作进行建模。它们由 async 和await 关键字支持。在大多数情况下,该模型相当简单:

  • 对于 I/O 绑定代码,您等待一个返回任务或异步方法内部任务的操作。

  • 对于 CPU 密集型代码,您需要等待使用 Task.Run 方法在后台线程上启动的操作。

wait 关键字是神奇发生的地方。它将控制权交给执行等待的方法的调用者,并最终允许 UI 响应或服务具有弹性。

I/O 限制示例:从 Web 服务下载数据

private readonly HttpClient _httpClient = new HttpClient();

downloadButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI as the request
    // from the web service is happening.
    //
    // The UI thread is now free to perform other work.
    var stringData = await _httpClient.GetStringAsync(URL);
    DoSomethingWithData(stringData);
};
Run Code Online (Sandbox Code Playgroud)

CPU 密集型示例:执行游戏计算

private DamageResult CalculateDamageDone()
{
    // Code omitted:
    //
    // Does an expensive calculation and returns
    // the result of that calculation.
}

calculateButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI while CalculateDamageDone()
    // performs its work.  The UI thread is free to perform other work.
    var damageResult = await Task.Run(() => CalculateDamageDone());
    DisplayDamage(damageResult);
};
Run Code Online (Sandbox Code Playgroud)

上面的示例展示了如何使用 async 和 wait 来执行 I/O 密集型和 CPU 密集型工作。关键是您可以确定您需要执行的作业何时是 I/O 密集型或 CPU 密集型,因为它会极大地影响代码的性能,并可能导致滥用某些构造。

在编写任何代码之前,您应该问以下两个问题:

您的代码是否会“等待”某些内容,例如数据库中的数据?

  • 如果您的答案是“是”,那么您的工作是 I/O 密集型的。

您的代码会执行非常昂贵的计算吗?

  • 如果您回答“是”,那么您的工作受 CPU 限制。

如果您的工作受 I/O 限制,请使用 async 和 wait而不使用 Task.Run。您不应该使用任务并行库。异步深入文章中概述了其原因。

如果您的工作受 CPU 限制并且您关心响应能力,请使用 async 和 wait,但使用 Task.Run 在另一个线程上生成工作。如果工作适合并发和并行,您还应该考虑使用任务并行库


归档时间:

查看次数:

161325 次

最近记录:

6 年,1 月 前