Manage your threads

刚开始programming的时候也不会考虑multithread之类的概念。只是顺序写出来就好,逐渐涉及到一些performance的问题时,好多情况需要了解清楚thread到底是被如何管理的,就是所谓的thread model,这里谈谈自己的体会。

Thread Pool

C++的simple thread pool https://github.com/progschj/ThreadPool
虽然程序简单,但是有很多的star,主要是方便定制方便集成。

使用thread pool的场景

  • server端的程序,过来一个request就从thread pool中取一个thread进行操作,然后返回结果。如果不使用thread pool的话,在很短的时间内有很多的request发送到server端,这个时候server会在很短的时间内启动很多的thread,这个时候cpu会频繁地进行threads之间的context switch导致实际的处理request的能力变低
  • thread 本身的逻辑比较复杂,没法用openmp那种不同的数据同样的code来执行。

Openmp

typical的使用场景是single node case 下for loop的优化,更多的是用于计算类的优化,比如reduction,sum之类的操作。

MPI

使用MPI最开始还是用于计算的任务,multiple node下的计算任务。虽然有很多的node,但是在大规模thread的环境下,这些node对于程序来看并没有什么本质的区别。code本身还是SIMD的样子,通过不同的rankid来对每个process进行标记。MPI使用的实际是multi-process,具体的通信也是multi-process之间进行通信。

run one rank on one node:

using mpi to control this

mpirun --map-by node -np 4

using srun to control this

srun -n 4 -N 4

MPI + thread pool

参考这个

MPI更suitable的场景是,使用multiple core on different nodes and let them do similar task to get the results which better for parallel.

遇到过一个in-memory的data base的项目,这个项目是MPI程序,每一个MPI process是一个独立的可以接受请求的服务,对于接受请求的服务来说,按理说一个node上启动一个就够了,因为这个program是管理memory资源的,一个program上只需要一个就能access到这个node上所有的memory资源,但另外一个问题是,这个服务没有很好用的rpc库,每个rpc都是一个单独地process,没法实现那种一个request过来,启动一个thread进行处理的操作。但是单独一个MPI process 在一个node上处理request的效率又很低,于是就每个NODE上启动4-5个MPI process,然后说这样可以提升一些request的处理能力。但这种操作相当于是把原来一个node上的完整的memory划分成了4-5份,然后每个node管理一份,很鸡肋。

这里感觉在archetecture的时候就没有考虑清楚,首先对于scalability,如果这是一个computing的项目,比如说是simulation或者是某个algorithm这种计算型很重的任务,scalability就是使用更多的core之后看时间会不会减少。

如果说这个项目是一个data base的项目,scalability应该是使用了很多的node之后,处理request的时间怎么样,然后throughput怎样。因为这里care的是memory资源而不是cpu core。

除非是那种每个process相似度很高的任务,可以直接不考虑communication或者data load 直接处理。对于更多的项目是comutation与network or IO 型的任务互相混合的。比如读一点数据处理一些任务,这个时候读数据是IO操作,处理任务是computation操作,比较好的是使用 MPI+cuda 或者 MPI+openmp 这里MPI的作用是保证每个node上启动一个program进行IO操作,因为MPI process提供了ID,进行distributed的管理也比较方便,比如服务发现啊,或者sharding,partition之类的操作。然后openmp或者thread pool进行 on node 的multithread 操作。

如果是很整齐的并行处理比如一个for loop那种,使用openmp/cuda这种模型比较直接,如果是网络的那种,有可能request多,有可能request少,使用thread pool比较实际。

总之是不同的工具适用于不同的情况。那种hybrid更像是hierachy的管理,group manager之间负责数据的通信和loading,group内部负责并行计算(一个节点上的thread)。而纯粹的MPI相当于是扁平化的跨节点的thread pool 只有core的概念没有节点的概念。

如果是单纯的server然后每个node上run一个,甚至不需要使用MPI,比如可以将ip写在parallel file system上,然后client把这些ip都存起来。或者在cloud的环境下就通过third party的工具比如etcd或者redis进行服务注册或者发现。

mpi的一个好处就是可以对thread进行id的分配,这样进行data partision之后hash计算也比较容易,这似乎是这个场景下唯一的好处了,因为通信的话显然用现有的rpc库比较实际。或者另外一种solution是把所有的ip注册在master process上(比如id=0)client从这个ip=0的node上进行服务发现,这本质上与使用third party的工具是一样的。

想想要是不用mpi或者third party的工具仅仅用parallel file system 还真不容易实现,因为要给没给server一个id,使用third party的话,先注册的就是id小的,然后可以自增,就是所谓的sorted key 使用MPI的化,本身就是自带了id。

TBB

task based parallelism

https://www.fz-juelich.de/ias/jsc/EN/Expertise/Workshops/Conferences/CSAM-2015/Programme/lecture7a_gonnet-pdf.pdf?__blob=publicationFile

http://theory.stanford.edu/~aiken/ecp/ecp.pdf

https://github.com/cpp-taskflow/cpp-taskflow

the flow graph
https://www.threadingbuildingblocks.org/tutorial-intel-tbb-flow-graph

go routine vs task parallel library
https://stackoverflow.com/questions/51962682/implementation-of-go-routines-vs-task-parallel-library

CPU Affinity

到cpu affinity级别的优化就是单节点上比较细粒度的优化,具体可以参考这一篇,其中有诸多关于c++ 使用thread提供的工具的例子,以及cpu affinity的技术细节,在实际的项目中常常没有用到很细节的优化,比如那种针对某个archetecture进行参数优化的情况。

具体涉及到的内容角较多,列出一些比较实用的点:

查看cpu的topology可以使用lstopo进行查看,可以很直接的看到各个cache的大小

具体c++11 thread 如何bind cpu 可以参考这里的示例代码,这里包括了通过代码查看cpu信息以及使用hwloc library。

context switch

https://stackoverflow.com/questions/21887797/what-is-the-overhead-of-a-context-switch

https://eli.thegreenplace.net/2018/measuring-context-switching-and-memory-overheads-for-linux-threads/

同时running 的thread的数目最好和core的数目一致,这样可以避免context switch

https://stackoverflow.com/questions/25933912/should-i-bind-spinning-thread-to-the-certain-core

https://stackoverflow.com/questions/14170127/pros-and-cons-of-cpu-affinity

cpu affinity
https://www.linuxjournal.com/article/6799

thread handle
https://stackoverflow.com/questions/24645880/set-cpu-affinity-when-create-a-thread

thread affinity and hyper threading
https://dzone.com/articles/c11-threads-affinity-and-hyperthreading-1

推荐文章