unw*_*ind 389
这非常直观:
如果CPU速度更快,则程序受CPU限制,即它大部分时间都花在CPU上(进行计算).计算π的新数字的程序通常是CPU绑定的,它只是处理数字.
如果I/O子系统更快,程序将受I/O限制.哪个确切的I/O系统可能会有所不同; 我通常将它与磁盘相关联,但当然网络或通信也很常见.一个程序通过一个巨大的文件查找某些数据可能会成为I/O绑定,因为瓶颈就是从磁盘读取数据(实际上,这个例子可能有点老式,现在数百MB/s从SSD进来).
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测量中.也许您可以做的最好的事情是估计缓存未命中.
也可以看看:
I/O bound指的是一种条件,其中完成计算所花费的时间主要由等待输入/输出操作完成所花费的时间来确定.
这与CPU绑定的任务相反.当请求数据的速率低于其消耗速率时,或者换句话说,花费更多时间来请求数据而不是处理数据时,就会出现这种情况.
异步编程的核心是任务和任务对象,它们对异步操作进行建模。它们由 async 和await 关键字支持。在大多数情况下,该模型相当简单:
对于 I/O 绑定代码,您等待一个返回任务或异步方法内部任务的操作。
对于 CPU 密集型代码,您需要等待使用 Task.Run 方法在后台线程上启动的操作。
wait 关键字是神奇发生的地方。它将控制权交给执行等待的方法的调用者,并最终允许 UI 响应或服务具有弹性。
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)
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 次 |
| 最近记录: |