# Pthreads,第 3 部分:并行问题(奖金)
> 原文:<https://github.com/angrave/SystemProgramming/wiki/Pthreads%2C-Part-3%3A-Parallel-Problems-%28Bonus%29>
## 概观
下一节讨论 pthreads 碰撞时会发生什么,但如果我们让每个线程完全不同,没有重叠怎么办?
我们发现最大加速并行问题?
## 令人尴尬的并行问题
在过去几年中,对并行算法的研究已经爆发。一个令人尴尬的并行问题是任何需要很少努力转向并行的问题。他们中的很多人都有一些同步概念,但并非总是如此。你已经知道一个可并行化的算法,Merge Sort!
```c
void merge_sort(int *arr, size_t len){
if(len > 1){
//Mergesort the left half
//Mergesort the right half
//Merge the two halves
}
```
通过对线程的新理解,您需要做的就是为左半部分创建一个线程,为右半部分创建一个线程。鉴于您的 CPU 有多个真实核心,您将看到符合 [Amdahl 定律](https://en.wikipedia.org/wiki/Amdahl's_law)的加速。时间复杂度分析也很有趣。并行算法运行在 O(log ^ 3(n))运行时间(因为我们假设我们有很多核心的花式分析。
但在实践中,我们通常会做两处更改。一,一旦数组变得足够小,我们就会抛弃并行 mergesort 算法并执行快速排序或其他算法,这些算法可以在小数组上快速运行(某些东西可以缓存一致性)。我们知道的另一件事是 CPU 没有无限核心。为了解决这个问题,我们通常会保留一个工作人员池。
## 工人池
我们知道 CPU 的内核数量有限。很多时候,我们启动了许多线程,并在空闲时为他们提供任务。
## 另一个问题,平行地图
假设我们想要将一个函数应用于整个数组,一次一个元素。
```c
int *map(int (*func)(int), int *arr, size_t len){
int *ret = malloc(len*sizeof(*arr));
for(size_t i = 0; i < len; ++i)
ret[i] = func(arr[i]);
return ret;
}
```
由于没有任何元素依赖于任何其他元素,您将如何进行并行化?您认为在线程之间拆分工作的最佳方法是什么?
## 调度
分离工作有几种方法。
* 静态调度:将问题分解为固定大小的块(预定义),并让每个线程都在每个块上运行。当每个子问题花费大致相同的时间时,这很有效,因为没有额外的开销。您需要做的就是编写一个循环并将 map 函数赋予每个子数组。
* 动态调度:当一个新问题变得可用时,有一个线程为它服务。当您不知道调度需要多长时间时,这非常有用
* 引导式调度:这是上述各种利益和权衡的混合。您可以从静态计划开始,并根据需要缓慢移动到动态
* 运行时调度:你完全不知道问题需要多长时间。不要自己决定,让程序决定做什么!
[来源](https://software.intel.com/en-us/articles/openmp-loop-scheduling),但无需记忆。
## 几个缺点
由于缓存一致性和调度额外线程之类的东西,你不会马上看到加速。
## 其他问题
来自[维基百科](https://en.wikipedia.org/wiki/Embarrassingly_parallel)
* 一次将 Web 服务器上的静态文件提供给多个用户。
* Mandelbrot 集,Perlin 噪声和类似图像,其中每个点都是独立计算的。
* 渲染计算机图形。在计算机动画中,每个帧可以独立渲染(参见并行渲染)。
* 密码学中的暴力搜索。[8]值得注意的现实世界的例子包括 distributed.net 和加密货币中使用的工作证明系统。
* BLAST 在生物信息学中搜索多个查询(但不是针对单个大型查询)[9]
* 大规模面部识别系统,其将数千个任意获取的面部(例如,经由闭路电视的安全或监视视频)与类似大量的先前存储的面部(例如,盗贼画廊或类似的观察列表)进行比较。[10]
* 计算机模拟比较许多独立场景,例如气候模型。
* 进化计算元启发式算法,如遗传算法。
* 数值天气预报的集合计算。
* 粒子物理中的事件模拟与重建。
* 行进方块算法
* 筛分步骤的二次筛和数字筛。
* 随机森林机器学习技术的树木生长步骤。
* 离散傅里叶变换,其中每个谐波是独立计算的。
- UIUC CS241 系统编程中文讲义
- 0. 简介
- #Informal 词汇表
- #Piazza:何时以及如何寻求帮助
- 编程技巧,第 1 部分
- 系统编程短篇小说和歌曲
- 1.学习 C
- C 编程,第 1 部分:简介
- C 编程,第 2 部分:文本输入和输出
- C 编程,第 3 部分:常见问题
- C 编程,第 4 部分:字符串和结构
- C 编程,第 5 部分:调试
- C 编程,复习题
- 2.进程
- 进程,第 1 部分:简介
- 分叉,第 1 部分:简介
- 分叉,第 2 部分:Fork,Exec,等等
- 进程控制,第 1 部分:使用信号等待宏
- 进程复习题
- 3.内存和分配器
- 内存,第 1 部分:堆内存简介
- 内存,第 2 部分:实现内存分配器
- 内存,第 3 部分:粉碎堆栈示例
- 内存复习题
- 4.介绍 Pthreads
- Pthreads,第 1 部分:简介
- Pthreads,第 2 部分:实践中的用法
- Pthreads,第 3 部分:并行问题(奖金)
- Pthread 复习题
- 5.同步
- 同步,第 1 部分:互斥锁
- 同步,第 2 部分:计算信号量
- 同步,第 3 部分:使用互斥锁和信号量
- 同步,第 4 部分:临界区问题
- 同步,第 5 部分:条件变量
- 同步,第 6 部分:实现障碍
- 同步,第 7 部分:读者编写器问题
- 同步,第 8 部分:环形缓冲区示例
- 同步复习题
- 6.死锁
- 死锁,第 1 部分:资源分配图
- 死锁,第 2 部分:死锁条件
- 死锁,第 3 部分:餐饮哲学家
- 死锁复习题
- 7.进程间通信&amp;调度
- 虚拟内存,第 1 部分:虚拟内存简介
- 管道,第 1 部分:管道介绍
- 管道,第 2 部分:管道编程秘密
- 文件,第 1 部分:使用文件
- 调度,第 1 部分:调度过程
- 调度,第 2 部分:调度过程:算法
- IPC 复习题
- 8.网络
- POSIX,第 1 部分:错误处理
- 网络,第 1 部分:简介
- 网络,第 2 部分:使用 getaddrinfo
- 网络,第 3 部分:构建一个简单的 TCP 客户端
- 网络,第 4 部分:构建一个简单的 TCP 服务器
- 网络,第 5 部分:关闭端口,重用端口和其他技巧
- 网络,第 6 部分:创建 UDP 服务器
- 网络,第 7 部分:非阻塞 I O,select()和 epoll
- RPC,第 1 部分:远程过程调用简介
- 网络复习题
- 9.文件系统
- 文件系统,第 1 部分:简介
- 文件系统,第 2 部分:文件是 inode(其他一切只是数据...)
- 文件系统,第 3 部分:权限
- 文件系统,第 4 部分:使用目录
- 文件系统,第 5 部分:虚拟文件系统
- 文件系统,第 6 部分:内存映射文件和共享内存
- 文件系统,第 7 部分:可扩展且可靠的文件系统
- 文件系统,第 8 部分:从 Android 设备中删除预装的恶意软件
- 文件系统,第 9 部分:磁盘块示例
- 文件系统复习题
- 10.信号
- 过程控制,第 1 部分:使用信号等待宏
- 信号,第 2 部分:待处理的信号和信号掩码
- 信号,第 3 部分:提高信号
- 信号,第 4 部分:信号
- 信号复习题
- 考试练习题
- 考试主题
- C 编程:复习题
- 多线程编程:复习题
- 同步概念:复习题
- 记忆:复习题
- 管道:复习题
- 文件系统:复习题
- 网络:复习题
- 信号:复习题
- 系统编程笑话