对于参数服务器,有一个parameter gathering的任务;对于多GPU多机训练,可以将某个GPU作为参数服务器来存储数据。每次forward/backward后计算所需/所得的参数,要通过参数服务器进行参数聚合。

Page3:这里的Pull(R)和Push(W)就是d2l,PS一节写的,对PS抽象的pull、push操作;

从Page3的d图可知,随着client threads的上升,吞吐量立刻达到了瓶颈。在吞吐量达到bottleneck的同时,CPU利用率基本上也达到了100%;但是吞吐量才大约20 Mops/s(Million operations / second)每秒的百万次操作(应该指参数服务器的read/write(?

e图可知,indexing的延迟所需时间较多,gathering很少。

但是目前的proj并不会要求很多indexing方面的内容;

参数服务器主要关注Embedding层模型的操作和推理,因为embedding层的规模较大;embedding层通过PS算出feature vector后,会被投入到inference server(通常是一系列MLP)

MLP因为规模较小(因为embedding vector -> label的过程,所以可以直接存储在inference server上)

PS系统使用PM hash indexes,index的键值key是特征ID、value是对应embeddings层(应该是以参数形式存在的)

PS会被暴露到clients,通过RPC接口来进行交互;

四个PM indexes: …

配置的系统有6个DCPMM作为PS,并且会持续不断发送Pull(read)请求;

每一个Pull对PS的操作进行读申请,申请的ID batch size是500

Zipfian分布:https://blog.csdn.net/m0_37816922/article/details/130482955 长尾分布,用这个分布来构造ID

除了indexing,参数服务器的CPU,会花29%的时间在gathering embeddings上(做参数汇聚)我们将其归因于,大的gathering batch size和缓慢的PM read延迟耗时;

PetPS提供两个接口:Pull(ID)来获得一个batch IDs的对应参数(后续会被推理服务器使用)

Push(IDs, embeddings) 该操作被用来更新一个batch的parameters

两个操作都通过RPC来实现;

RPC的简要说明介绍:

https://blog.csdn.net/WangTaoTao_/article/details/102676008

PETHASH:

(1)key是feature的ID,同时values值本质上保存了指向对应embedding层的指针;(内存地址)

(2)NIC Gathering:

NIC:Network Interface Card 网络接口卡

NIC Gathering是利用NIC网络接口卡的DMA设备,来进行参数汇聚,同时将这些收集到的参数返回给客户;

下面是一个具体的PetPS处理PULL请求batch的操作:

(1)PetPS首先将Pull的一个batch的ID,首先放到了Request Pool中(在client thread较多的时候,可能要执行排队等待的操作);一个Pull操作的形式,后面跟着一串访问的ID,标记为:ID0,ID1,ID2,…..

(2)然后这些ID会作为key在PETHASH中检索对应的pair,hash pair存的内容value就是对应embedding层的内存地址,以及这个embedding层的大小。这个过程就叫indexing;(找到对应Embi的<ptri, sizei> 的大小)

(3)在indexing结束后,我们需要通过Gathering,来将找到的index对的emb做整合;这一步上,传统的PS往往用CPU来执行Gathering,这会带来CPU利用率的问题;PetPS中使用NIC Gathering来做这一步的寻找和聚合,具体的,通过NIC的DMA引擎,以DMA方式直接从Mem Pool中对应的ptr处拷贝embedding层的内容,并最终按照request的顺序完成Gathering,将这个gather好的emb向量返回给inference server,用以后续MLP层等的计算;

3.2 PetHash组件:

许多之前的Persistent Memory哈希index通常会采用多层次结构,以减少rehashing的行为;但是会付出多次PM read的代价;

例如,当前最好的hash table算法Dash,需要多次

多级哈希表,类比多级页表进行查询的行为。

需要多次pointer-chasing操作,来通过多次指针解引用找到对应的一个KV pair;

PetHash的关键核心在于,减少每次indexing中所需的PM read的数目到1,通过利用PS参数服务器特殊的负载特征;

(那相对于多级结构,自然采用的是单层次结构了…..不会相应的tradeoff,是不是会带来hash table容量的减少呢?)

… TODO

3.3 NIC Gathering

在indexing后,PS的下一步是基于lookup的index的结果来继续,并将他们送回给client;

conventional way: CPU gathering 因为PM read的高延迟,会浪费很多CPU周期;

PetPS:提出用NIC Gathering

现代网卡NIC具有scatter-gather DMA方式;

这个功能可以通过改造来完成gathering embedding的任务;

Fig5将传统的CPU Gathering和PetPS的NIC Gathering做了对比;

Naive的方式,如CPU gathering对应的图。具体的步骤是,CPU按序将PM中的embeddings拷贝到内存DRAM中;然后,NIC再从内存DRAM中拷到client去;Gathering会占CPU时间;

NIC Gathering时间:CPU会向NIC发送一个SG-DMA 描述符列表(每个SG-DMA描述符对应一个embedding在PS中的组织)然后NIC接收到了这个SG-DMA描述符列表后,逐一直接从PM拷贝embedding内容;

DMA是被硬件异步调度的,所以任何对DMA化的embedding执行并发更新或内存回收都会带来数据的不一致性,PetPS有一种来避免这种非一致性的能力;

那在PetPS和CPU Gathering之间切换,可能也会带来这种数据的非一致性?只管上,切换两种方式一个前提是,能将之前的数据非一致性避免掉;

现代NIC的轻量级DMA引擎可以在很低的运行时代价情况下完成零拷贝数据(数据分发和数据聚合)

编程接口是一个描述符列表,这个描述符列表是通过用户空间网络库(例如Intel DPDK)和libibverbs

每一个描述符都包含了某个内存块的源地址,以及其大小;

软件会提交一个描述符列表到NIC的DMA引擎来初始化DMA;DMA引擎会将描述符列表中的内存块做合并,以完成网络转发。Scatter-gather DMA是一开始被设计用来优化MPI communication,来消除额外的HPC大数据内容拷贝;

PetPS改动了它,使得它能够gather embeddings,同时不对硬件做修改;

卸载embedding gathering到NIC上:为了利用DMA引擎来完成不同size的embedding内容的传输,同时处理丢失的embeddings;PetPS提供一个压缩的DMA-capable(DMA能接受的)的报文格式,每段报文还有两个域:header部分和payload部分,header首部有missing embedding的数目以及他们的特征ID,同时payload会将所有请求并且能够在当前参数服务器中获取的embedding进行压缩;

如果在payload field中有n个embeddings,在PetPS中聚合一个报文会需要1+n个DMA周期;

Exp: #3 NIC Gathering

fig10展示了PetPS对CPU时间的下降的功能,随着两类不同的gathering schemes,在他们的峰值吞吐量,有如下发现:

(1)NIC Gathering会将gathering的时间从180 us减少到14us,证明了NIC Gathering消除了CPU用来gathering的开销;

(2)有了NIC Gathering,indexing部分的开销也获得了22%的提升,因为NIC Gathering避免了污染CPU本身的cache;

(3)CPU通过NIC Gathering能获得1.2倍的峰值吞吐量提升;

(4)注意到NIC Gathering也可以作用于DRAM,我们将PS的介质从PM替换到DRAM来做重复实验(也即将DRAM-PS的CPU Gathering策略替换为NIC Gathering)

有意思的是,我们发现了NIC Gathering反而造成了30%吞吐量的降低,我们发现的潜在的原因是,DRAM的低延迟读写能力使得DMA引擎反而成为了性能的瓶颈。

P99——99%的请求的延迟与耗时。

对于

Exp #7,也即Fig 14

注意到,这类图随着吞吐量横轴的上升,纵轴Median Latency也在同步升高。具体到某个峰值throughput的时候,median latency会急剧上升,这是因为此时request pool中已经开始出现排队现象了,所以延迟会直线上升。因此,出现排队的对应吞吐量横坐标也就是我们的peak throughput!

Topic: Introduced by the slide P26

接下来是RTFSC和跑demo的流程

阅读wq_ps_server.cc:

关于gflags, 命令行参数在程序中的config;

https://gflags.github.io/gflags/

(注意不要在两个文件中对同一个命令行参数做定义,可能会在链接的时候造成影响)

先把demo跑起来!

具体的和Gathering有关的RPC框架,以及CPU Gathering、NIC Gathering的内容在src/ps/wq_ps_server.cc中;

然后要跑,就在/benchimark下面的bench.py,运行这个脚本;

对应生成的数据在/benchmark/log下的exp…中了,有client和ps的(对应客户线程和服务器

注意,要对实验配置的参数做修改,则在/benchmark/exp_config.py下面来进行设置。就在class ExpDRAM中的内容。

SERVER_CONFIGS中的内容不需要改

CLIENT_CONFIGS中的内容可以自己改;

在binding中的thread_num,thread_num就是client端的线程压力;

async_req num就是协程个数(每个进程会有对应的影响;

line533开始;

然后画图脚本就是paper.ipynb,这里用的就是之前log中获得的数据;

怎么跑bench.py,好像也要改(

只使用DRAM时,CPU Gathering直接将内存中的embeddings拷贝到一个buf,然后buf送NIC;NIC送client

NIC Gathering是每个interval构造一个Scatter-Gather描述符表,送NIC,然后NIC从DRAM对应位置拷到RDMA buffer里,送client

随着interval减小,描述符表的构造更加频繁,DMA控制消耗更高,吞吐量达到瓶颈;

而CPU Gathering会打满CPU利用率,但其吞吐量更高。

根据interval设计规划算法?

思路一:

对于每次gathering,额外算上一个interval期间的throughput。

client的线程数和interval并不是纯反比关系。在request到来的interval时间大于indexing所需时间(也即处理一次batch的request耗时)时,显然系统可以在单位时间内处理更多的indexing任务(也即工作不饱和)但是client的线程数增长到某个地步的时候,其Pull操作request的间隔小于indexing的时间,此时不论interval如何再变小,吞吐量不可能有变化(反而latency爆炸增长,在Request Pool中排队)

所以,对于PM,其有效的interval应该是:
$$
[t_{PM}, +\infty)
$$
对于DRAM,其有效的interval应该是:
$$
[t_{DRAM}, +\infty)
$$
然后,随着client线程数目的波动,gathering任务的interval应该在上述区间间隔。

再考虑,同种介质,不同gathering scheme。对于PM来说,其CPU Gathering和NIC Gathering,假设其图像交点为t1,也即在0-t1内,interval小,NIC Gathering的DMA通讯是整个系统性能的bottleneck,CPU Gathering策略好;在t1-+inf,interval大,NIC Gathering的性能好,DMA通讯在tradeoff中对系统性能影响小,CPU Gathering会浪费CPU周期在gathering、cpu cache污染等上;

但是,注意到interval是有下界的,实际情况中有:
$$
t_{PM} > t_1
$$
interval间隔一定落在NIC Gathering更优的一侧,所以用NIC Gathering是能优化的;

反之,在DRAM-PS上,相同概念假设CPU gathering和NIC Gathering的效率一致的interval时间为t2,则有:
$$
t_{DRAM} < t_2
$$
所以,在DRAM的client线程数低,导致interval较大,位于:
$$
[t_{2}, +\infty)
$$
之间时,NIC Gathering效率高;

当DRAM的client线程数高,导致interval较低,位于:
$$
[t_{DRAM}, t_2]
$$
之间时,反而是CPU Gathering的效率高;

所以逻辑上,只要能计算出DRAM的t2,那在每一个interval状态的时刻,我们可以直接在O(1)时间内选择应当选的决策;

因为这个interval肯定不是固定的,会随着client的强度而改变,所以我们要实现的系统应该是能够自适应调整gathering scheme的;

(1)第一想法:很像计算机网络中的TCP协议啊!网络拥塞的时候,发生…..网络不拥塞的时候,用….策略。堵塞与否的状态,和interval(request到来的大小)是很类似的,interval小,可以理解成网络链路阻塞的时候;interval大,可以理解成网络链路空闲的时候。一般情况我们是解不出来这个实际的t2的,因为系统状态等等复杂的外部条件,获得精确的解是很难的。

与TCP中AIMD的区别:

AIMD设置了CongWin,控制窗口的大小,这个会直接影响发送效率;我们这里,影响发送效率(形式上为吞吐量)的方式,只有NIC Gathering和CPU Gathering之间相互转换这一种方式。

(2)

如果系统一段时间内的interval的变动不大(可以计算一定时间内,interval的方差,小于某个值就采取策略A)

策略A:每隔时间t,分别在第i和i+1个来的gathering task中,在ti到ti+1时间段用NIC;在ti+1到ti+2时间段用CPU gathering。选择两者中吞吐量较大的作为接下来时间间隔t内的策略;

如果系统一段时间内的interval的变动较大。

  1. 如果interval变大,也即来的gathering频率降低了,如果此时是NIC Gathering,不变(因为要变也是CPU Gathering变NIC Gathering)如果此时是CPU Gathering,那么在接下来两个周期内

    尝试在下个interval内用NIC Gathering,如果吞吐量变大,就用NIC Gathering;如果

那可以一直采用一种。达到一个时间的threshold后,在下一个interval间切换成另一种。如果吞吐量上升了,那就保持,重置计时器。反之,立马切换回去;

(3)

维护一个区间[l, r],在l到r之间用CPU和NIC都可以;

在l左侧用CPU;在r右侧用NIC;

interval = max(interval, 最小indexing时间)

我们只讨论DRAM。

每隔一段时间,

要是能求得当前状态下图像的交点就好了,然后对于系统接收的request间隔。如果是

要是有版本库的问题:

可以在cmake/make下开VERBOSE具体去看一看是那一条相关指令报错。

报错的话,可以先用find / | grep fmt.a 这类指令去看所有的库,如果有多个最简单的做法是全部删掉;然后再下一个,此时就是只有一个库了;此时,如果还有问题,那么就是库的版本有问题。此时的做法是去找对应的版本,然后再尝试编译;

很好用的检索版本库的命令:

1
find / | grep parallel_sort.h

查看所有进程并杀死一些卡死的进程:

1
2
3
4
5
6
7
top
kill [pid]


ps aux | grep [proc_name]
找到pid后
kill [pid]

编译跑通:

用140和141相互连,改能用的服务器;

ssh无密链接:https://blog.csdn.net/jeikerxiao/article/details/84105529

已完成

对于/etc/mtab:

The */etc/mtab* file contains the currently mounted filesystems. It’s used by the mount and umount commands to mount, list, and unmount the volumes.

The content structure of the *mtab* file is similar to the */etc/fstab* file. However, the mtab file is not used by the kernel, which maintains its own list, i.e., /proc/mounts and /proc/self/mounts. In some systems, the *mtab* file is a symlink to */proc/mounts.*

The /etc/mtab file has six columns. Here’s a typical /etc/mtab file:

(1)NFS

10.0.2.130:/home/xieminhui/petps和当前服务器的/home/xieminhui/petps建立了NFS

我们要在10.0.2.140上建立10.0.2.141:/home/huangjr/hjr-petps/petps的NFS关联

这里fusermount失败的原因是,没有正确建立NFS,导致在/etc/mtab中没有维护其对应的filesystem的信息;

上面,有

搭建NFS LAN网络共享文件夹

记录我们具体在两台服务器10.0.2.140和10.0.2.141间共享文件夹的操作:

(1)首先选定10.0.2.141为我们的master server,10.0.2.140作为我们的client server;

(2)在主机10.0.2.141上:

1
sudo apt install nfs-kernel-server #下载nfs内核服务器

然后:

1
2
3
sudo chown nobody:nogroup /home/huangjr/hjr-petps #设置待共享文件夹的文件持有性质,没有任何人的分组
sudo chmod 777 /home/huangjr/hjr-petps # 设置待共享文件夹的权限为,全体都可以读写修改
vim /etc/exports #修改出发端口的情况

具体的,我们要将发出端口的内容修改为如下所示的内容;

1
/home/huangjr/hjr-petps/petps 10.0.2.140(rw,no_root_squash,async)

然后激活所有的转发出口

1
2
sudo exportfs -a #making the file share available 
sudo systemctl restart nfs-kernel-server #重启NFS系统服务s

这样就完成了server端的部署

再接着,我们需要完成client设备10.0.2.140主机上的设备部署

首先:

1
sudo apt install nfs-common

下载好对应服务包

然后假设要共享的对应文件夹为hjr-petps-client

1
sudo mkdir hjr-petps-client

然后:

1
mount -t nfs -o rw 10.0.2.141:/home/huangjr/hjr-petps/ /home/huangjr/hjr-petps-client/

用该指令通过nfs将两者进行关联,前面的是服务器对应的映射文件夹,后面是client端的同步文件夹

这样在client上通过mount或df -h,就可以查看到我们的NFS情况了。

我们理一下代码顺序:

首先在bench.py的main中,经过一个初始化的Pnuke,输出一次petps_server和benchmark_server;

再然后,经过bench.py 89-91行代码,设置日志文件、实验列表等等;

下面用enumerate维护了需要实现的实验列表;

同时遍历了ALL SERVERS… 的所有非master服务器;

将其进行配置;

这里用swapoff关闭了所有设备的swap功能;

接着

然后开始each.runExperiment()开始跑实验;

—->进入ParameterServerExperiment类,在其中调用_BeforeStartAllRun这个函数;

首先打印了一条pnuke petps_server/benchmart _client

然后分别打印了可以选用的服务器;

接着处理一系列对server配置的解析:

也即server config和client config

处理解析是用PreprocessConfig函数,

config中如果有binding

接着在line 173,输出运行的实验id为9(

然后通过三重循环构造配置信息,最后在维护的runs列表中逐一进行yield来调用;

/dev/shm 是DRAM

/media/aep0/ 是PM

具体的问题是:

server 0 没有:

I connect server 1

也就是ps_0中没有这条记录!

那也就是connectNode的时候出现了问题。

interval其实是不会变的

然后,不同的request,用CPU Gathering和NIC Gathering处理的效率也是不同的。假设间隔request的interval是固定的

NIC Gathering:年轻人干活,但是需要DMA调度的cost

CPU Gathering:老年人干活

两种做tradeoff

现在的问题是,没有一个明确量化的能用来作为”代价”的量。

将Ubuntu从18.04提升到20.04的时候,可能需要手动upgrade未升级的包,然后再用更新工具,才能检测到新版本。

升级过程中存在一点中断现象

Linux的lsof——list open files可以用来展示某个文件具体的所有者

对于Linux中使用apt、apt-get时出现的锁问题,我们可以先用sudo lsof [对应所文件的路径] 查看所有打开这个所文件的对应的进程,然后手动kill掉他们;

memcached的安装教程:

1
https://linux.how2shout.com/install-memcached-on-ubuntu-20-04-lts-linux-server/

netstat用于监听网络信息接口:

如:

1
netstat -tulpn

-t, -u表示选用tcp、udp作为协议;-l表示listening监听;-p表示监听接口;-n表示Show numerical addresses;

  1. DSM construct的过程有问题

两个问题:

(1)DSMkeeper构建失败了

(2)在client_0的log里面,提示了qp状态转换失败了(可能RDMA框架有问题?)

最后总结起来是RDMA搭建也即网路的问题;现在已经ok了!

2024/1/17

重点:

(1)再把PetPs的一些细节搞懂,如indexing、CPU gathering、NIC gathering

(2)看懂wq_ps_server.cc的代码

(3)跑通demo,看画图脚本,画图

重点关注:

wq_ps_server.cc的内容

  1. 参数服务器PS;PM和DRAM的区别
  2. CPU gathering 和 NIC gathering
  3. 根据线程带来的阻塞情况,分配CPU gathering和NIC gathering的情况

2024/1/20

成功跑出来PetPS正确的吞吐量和数据了;

Q & To research:

  1. 设置use_dram=false,那这个shm path设置的是DRAM的路径?这里作何解释?

注意到,不同的request,其对request的处理单位是Mreq/s,应该是单位时间内处理的request数目?

阅读benchmark_client_common.h源码:

从第51行的Run函数开始就是具体的内容了;

https://en.cppreference.com/w/cpp/memory/unique_ptr

https://en.cppreference.com/w/cpp/language/auto

代码中的args_.thread_count_表示目前作为client server的线程数;

然后通过遍历这thread_count_个thread的内容,可以计算出这些client所有的request数目之和?

应该是每个线程单独去试着访存?

下面,第149行中,args_.batch_read_count_这里所执行的KV吞吐量Mkv/s通过操作数cap * 一次操作所需要读取的数量(也就是每次read的count)

看起来,这个吞吐量取决于

注意:计算throughput的时候,在Mreq/s(单位时间内处理的request数目)相同的时候,其Mkv/s(单位时间内读取的key-value)是不同的;

这一步,应该是在indexing啊,从request poll中根据要求的request来做indexing,找到k-v pair的v(也就是(emb_i的ptr, emb_i的size))

现在支持:

PM的CPU gathering(SHM_PATH = /media/aep0, use_dram = false, use_sglist = false)

(SHM_PATH = /dev/shm, use_dram = false, use_sglist = false)

不支持:

PM的NIC gathering(SHM_PATH = /media/aep0, use_dram=false, use_sglist = true)

(SHM_PATH = /dev/shm,use_dram = true, use_sglist = false)

共享内存: IPC进程通信

https://www.boost.org/doc/libs/1_47_0/doc/html/interprocess/sharedmemorybetweenprocesses.html#:~:text=XSI%20shared%20memory-,What%20is%20shared%20memory%3F,without%20calling%20operating%20system%20functions.

zero-copy:

https://en.wikipedia.org/wiki/Zero-copy

2024/1/21

Todo:

  1. Paper Reading:Cornflakes(done)、RDMA related papers
  2. PetPS:Project Code Learning
  3. PetPS:paper.ipynb等画图的细节需要

这个问题应该是:

如何调度使得Data serialization的效率最大化;

其他的一些Data serialization的方法:RESP(或许可以参考其中的优化和设计?),因为RESP用的是TCP协议栈,利用不了InfiniBand的硬件能力,throughput天然没有RDMA的高;

Cornflakes中强调的一个更贴近问题的过程:Data movement,为了实现传输,可能需要将内存中scattered的内存,gather到一个连续的I/O buf中;

在Cornflakes的paper中,提到了协议必须是hybrid的,因为zero-copy为了保证DMA的有效,存在维护memory safety和memory transparency的overhead;对于非kernel pinned的pages(也即可能被从内存中换出的页)是无法开启DMA的,如果要copy的数据来自这部分页,就需要通过memory copy;

https://medium.com/@penberg/on-kernel-bypass-networking-and-programmable-packet-processing-799609b06898#:~:text=Kernel%2Dbypass%20networking%20eliminates%20the,kernel%2Dbypass%20architecture%20in%20use.

SG和Copy的Throughput的对比:

形式上来说,Payload Size越大、Request的次数越少,使用Scatter-Gather DMA的效率越高。

Questions:

  1. Codepath怎么翻译….

Cornflakes中对 SG-DMA based Copy 和 Naive Copy的决策

fig 5.2:

Payload Size越大,对应DMA相比simple memcpy的效率越高;

Request次数越少,因为维护Scatter-Gather DMA的software overhead代价越低;

在Cornflakes中,显示大约individual fields在512 bytes是一个threshold。也就是这个时候,可能SG-DMA的 Copy所需要的时间就和Naive CPU based Copy所用时间一样了。

注意:在Cornflakes中,当且仅当需要传输的内容(也即field size)至少512 bytes才会开启scatter gather DMA来去传输field;

5.3节分析了影响这个Threshold的因素:

(1)维护memory safety(也即use-after-free等的代价,例如reference count…所有的zero-copy传输必须经历的时钟周期代价)

(2)Ring Buffer API(也就是将额外的scatter-gather entry添加到环形缓冲区所需要的时钟周期)

(3)拷贝数据块的速度(包含预取)

Threshold上升的影响因素:

  1. 拷贝内容的代价降低(例如通过custom offload)
  2. 添加一些额外的scatter-gather entry代价更高

Threshold下降的影响因素:

  1. 如果memory safety的codepath代价变低了,那么threshold就会下降;

其他会影响tradeoff的因素:是否有更多的offloads被NIC使用(例如encryption或RDMA,一般RDMA对NIC资源的占用是更高的,说RDMA是强NIC资源密集型的高性能网络协议)

Cornflakes里面使用的threshold是static的,然后在Limitations and Future Works里面提出了可以使用dynamic threshold(根据memory bandwidth )

然后在Section 6 Experiments部分所用的Cornflakes来跑实验,所有的数据都是建立在threshold为512的基础上实现的。

准确地说,这个threshold的设置是用Total Payload Size / Number of Element Queries来完成的。注意到当均摊单位request所需copy的bufsize超过512 bytes的时候,开sg list是高效的;

当均摊单位request所需的bufsize小于512 bytes,此时开sg list是低效的;

考虑如何确定PetPS中的threshold?总体思路

(1)可能是一个调参问题?看跑出来的曲线如何效率高;

(2)注意到这个调参效率其实是很高的,因为可以具有threshold带来的性能具有单调性,在n轮实验内就能确定范围[0, 2^n] bytes的threshold设置是否合理。而Cornflakes里面其实也是做了很多组实验的,所以这个我们可以自己多跑几组实验来研究一下;(那么,最naive的做法就是来试着调参喽,Cornflakes的fig5也是调参的过程)

(3)然后假设确定了threshold,如何进行hybrid的选择呢?对于pethash获得的所有value pair(也即emb_i在PM中的ptr,或者说PM中的地址),以及这个request所需copy的大小size_i

我们求一下均摊的individual payload size即可:
$$
\frac{\sum_{i = 1}^{batchsize}emb_i.size}{batchsize}
$$
如果大于(2)中计算的threshold,那么就用SG DMA;如果小于(2)的threshold,就用Naive的CPU copy。

当然,也有一些额外问题….比如计算individual的payload size,这个计算的过程是否会成为新的bottlenect,是否用batchsize个threshold的大小比较即可做到?

或者说,一个request pool的batch内,是否可以x个request用sg DMA;另外y个request用CPU gathering?

(1)PetPS画图的一些细节:吞吐量具体的表现形式

注意到在wq_ps_server.cc中,value timer计时是服务于Gathering的。所以生成图像的value部分对应gather耗时;

然后index timer对应服务于index部分耗时;

Get Parameter所用的时间,应该就是整个系统的吞吐量;

Latency metrics play a critical role in evaluating the performance of your applications and services. By observing P90, P95, and P99 latencies, you can identify potential bottlenecks, optimize user experience, and ensure that your systems achieve high performance for the majority of users.

Latency测量在应用和服务的性能评估方面发挥了很重要的作用,其中P99延迟的意义是:

99th percentile:P99 metric表明了99%的requests是可以在记录延迟中完成的,这个数据表明了,只会影响很小部分的users.

数据中Mean、P99都是对应部分的延迟;

如index part_mean就是完成indexing的时候所消耗的时间;

value part P99就是完成99% Gathering所需要消耗的时间;

接下来的实验,分别测试PS的:

(1)PM介质,开SG D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
df_dram_cpu = parse_logs_to_df('../benchmark/log/DRAM_PS_CPU_GATHER')
df_dram_cpu.groupby(['client_dataset', 'server_use_sglist']).agg([max, np.mean])[['TPS_Mkv/s', '[D] Value Part_Mean', '[D] Index Part_Mean']]

df_dram_nic = parse_logs_to_df('../benchmark/log/DRAM_PS_NIC_GATHER')
df_dram_nic.groupby(['client_dataset', 'server_use_sglist']).agg([max, np.mean])[['TPS_Mkv/s', '[D] Value Part_Mean', '[D] Index Part_Mean']]

df_pm_cpu = parse_logs_to_df('../benchmark/log/PM_PS_CPU_GATHER')
df_pm_cpu.groupby(['client_dataset', 'server_use_sglist']).agg([max, np.mean])[['TPS_Mkv/s', '[D] Value Part_Mean', '[D] Index Part_Mean']]

df_pm_nic = parse_logs_to_df('../benchmark/log/PM_PS_NIC_GATHER')
df_pm_nic.groupby(['client_dataset', 'server_use_sglist']).agg([max, np.mean])[['TPS_Mkv/s', '[D] Value Part_Mean', '[D] Index Part_Mean']]







2024/1/22

Problems To Solve

  1. 跑默认参数PM,反而PetPS没跑过DRAM-PS(猜测可能被memory bandwidth pressure影响了)

研究一下DecoupleAnalysis的脚本…

首先对于非list类型的,把他们变成list类型;

然后

用平均数还是

(1)Static zero-copy threshold:Cornflakes的方法

(2)Dynamic Zero-copy threshold:考虑到memory bandwidth pressure,threshold是会变动的;可能需要设计相应的动态threshold?

(3)最终的目标应该是最大化client thread变动的情况下,TPS_Mkv/s

部分实验数据

在线程数都是36的情况下,此时的DRAM性能远超PM。这是合理的,因为36逐渐降低的时候,小interval的部分占TPS_Mkv/s的权重贡献较大,所以导致DRAM性能全面碾压PM。对应的,我们期望threads数目在18的时候,能够获得更加合理的数据;

exp1: Huge Request effect on SG-DMA based zero-copy and Naive memory copy

这部分的目的是尝试在PetPS上将cornflakes中section 5部分的内容进行扩展,cornflakes上测试SG-DMA和naive copy之间throughput相等的threshold基本上是线性的。也即只要individual buffer size是512 bytes的时候,两者的效率相同;

但是cornflakes里面测试的Number of Elements只有1-32,我们最好测一下1-500这个大小,来看随着batch size的增加,在SG-DMA注册维护所需的overhead增加的同时,这种zero-copy带来的benefit能否offset掉其overhead;

具体方法:

(1)exp_config.py里面修改测试的CLIENT中的配置参数batch_read_count,从而在一次request中开启更多的SG-DMA;

要研究从Total Payload Size的方面,也就是x轴的影响,就固定CLIENT中的batch_read_count,然后修改value_size和key_space_m;

目前的想法是,固定CLIENT_CONFIG里面的batch_read_count,然后修改COMMON_CONFIG的value_size为128,256,512,1024,2048…etc value_size是参数的大小,会影响单个request请求copy的payload size大小,进而影响开启SG-DMA时的效率;

(2)要研究纵轴对throughput的影响,就在固定COMMON_CONFIG的同时修改CLIENT_CONFIG的内容,也即将batch_read_count从

batch_read_count <= max_kv_num_per_request

也就是一个batch读取的key-value pair数目不能超过COMMON_CONFIG中的中设置的最大值;

先测试一下batch_read_count变化的影响;

eg.1 对比batch_read_count从100变到500

首先,PM PS的CPU Gathering吞吐量是下降的,因为不开NIC Gathering;

PM PS的NIC Gathering,100->500

最大吞吐从7.3376->7.3198,平均吞吐从6.290836->6.434763,这是变大了(?

每一个坐标,肯定要测CPU gathering和NIC Gathering;

然后,分别对于介质DRAM和介质PM,需要分别测一个对应得表

我们先测DRAM介质的表!

这个in-depth measurement study

Paper:

Breakfast of Champions: Towards Zero-Copy Serialization with NIC Scatter-Gather

Section 3.1部分描述了Scatter-Gather DMA的entry是如何影响性能的,对于entry的大小远小于256 bytes的情况,是很可能会损害性能的;

Total Payload Size =

用(batch size, value, size)算一个correlation coefficient?

可以用几个相关性系数,分别算一下,然后说明我们取这个threshold的合理性;

p-value检验?

Pearson correlation?

https://zh.wikipedia.org/wiki/%E6%96%AF%E7%9A%AE%E5%B0%94%E6%9B%BC%E7%AD%89%E7%BA%A7%E7%9B%B8%E5%85%B3%E7%B3%BB%E6%95%B0

https://www.cnblogs.com/shona/p/12430665.html

用统计三大相关系数算一下得到的表格里面的值,尝试得到batch_count_size和value_size之间的关系。然后用这个关系来设计threshold;

(1)static zero-copy threshold:cited from Cornflakes;

(2)dynamic threshold design and implementation…

From Cornflakes, the memory bandwidth pressure can impact the threshold;

  1. 有什么东西能反映memory bandwidth pressure?将其量化后作为sample factor…

Total 应该是:batch_get_kv_count * value_size,这算做一个request的batch所需要做的Total Payload Size

这样;

其实横坐标应该是Total Payload Size / Query Number

看起来像是正态分布(

然后threshold设成\mu就行….

理想情况下,应该threshold比较近,然后因为现实中存在memory bandwidth pressure等因素,所以threshold ~ N(mu, sigma^2) 正态分布;

从histogram能够看出来

2024/1/23 星期二

  1. 研究一下paper.ipynb中画脚本的方式,处理数据到csv、处理csv中数据或其他来构造相类似的热力图;

设置index为True或False影响的是每一行数据最前面有没有一个序列号;

要计算的横坐标考虑client batch read count,纵坐标考虑client value size,然后计算的内容是在x, y相同的情况下,对server _ use _ sglist为True和False两种算一个比率

在client.cc里面有随机gather指令的部分,尝试用这部分内容来做单gather的操作;

一开始:

client发送内容到server,然后server根据client发送的报文,用indexing过程解析后,给values….

实际上设置fake_kv就行

先测的是DRAM的:

画2个超大的8 * 8 heat map

第一个heat map:CPU Gathering和NIC Gathering的perf 对比

第二个heat map:CPU Gathering和Hybrid Gathering的perf对比

然后,最理想化的算法,应该不出现performance的损耗,一般来说越好的算法,其boost的面积部分越高!

CPU Gathering和NIC Gathering的perf heat map

CPU Gathering和Hybrid Gathering的perf heat map

NIC Gathering和Hybrid Gathering的perf heat map

对不同value_size和batch_read_count都能达到较好的performance,说明对于更多的模型和embedding都能更好地适应,并提升维护对应模型参数服务器的性能。

然后,对于CPU Gathering和NIC Gathering下,分界线对应的就是threshold应该设置的地方;可以算SROCC、PRCC等统计学系数,说明我们所选static zero-copy threshold的合理性;