add report

This commit is contained in:
RistyTang 2024-10-21 08:28:05 +08:00
parent 225503039f
commit 4f01b5f3ec
10 changed files with 361 additions and 8 deletions

View File

@ -251,9 +251,9 @@ class LinUCB(ScheduleFrame):
first_aver = sum(first_half) / len(first_half)
second_aver = sum(second_half) / len(second_half)
# print('{} --> {}, {}'.format(first_aver, second_aver, abs(second_aver - first_aver) / first_aver))
if abs(second_aver - first_aver) / first_aver > 0.20:
if abs(second_aver - first_aver) / first_aver > config.WORKLOAD_CHANGE:
# workload change
print('----- test workload change -----')
print(f'----- test workload change -----')
self.reset()
else:
self.history_reward.pop(0)

View File

@ -2,10 +2,10 @@ import numpy as np
import matplotlib.pyplot as plt
# 生成数据
x = np.linspace(0, 6000, 1000)
x = np.linspace(0, 20000, 1000)
# Sigmoid 函数
def sigmoid(x, k=0.002, x0=3000):
def sigmoid(x, k=0.0003, x0=13000):
return 1 / (1 + np.exp(-k * (x - x0)))
# Tanh 函数

View File

@ -13,9 +13,11 @@ APPROXIMATE_CONVERGENCE_THRESHOLD = 150
PROBABILITY_THRESHOLD = 80
# It takes time for rewards to level off, only after this threshold, detection begins for load changes
LOAD_CHANGE_THRESHOLD = 200
LOAD_CHANGE_THRESHOLD = 50
# The size of history reward sliding window
HISTORY_REWARD_WINDOW = 60
HISTORY_REWARD_WINDOW = 50
# Basis for workload changes
WORKLOAD_CHANGE = 0.30

View File

@ -1,7 +1,8 @@
import numpy as np
# unet3d x0 设置为 3000对于 resnet50 x0 设置为 30000
def sigmoid(x, k=0.002, x0=30000):
# s3fs x0 设置为13000, k = 0.0003
def sigmoid(x, k=0.0003, x0=13000):
return 1 / (1 + np.exp(-k * (x - x0)))
# 接收curve发送的request解析为pool_and_size, pool_and_throughput
def process_request(request):

BIN
doc/image/OLUCB_CS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 356 KiB

BIN
doc/image/serial_read.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

350
doc/技术报告.md Normal file
View File

@ -0,0 +1,350 @@
# 一、引言
JYCache是一款面向个人使用、大模型训练推理等多种场景适配大容量对象存储等多种底层存储形态高性能、易扩展的分布式缓存存储系统。对每台推理用机其内部维护一个“客户端缓存模块”包含独立的读写缓存组件和数据源访问组件。通过层次化架构、接入层优化、I/O优化等多种组合优化JYCache 不仅支持文件顺序/随机读写,其读写性能也领先于国际主流产品 Alluxio。但是由于设计中读写缓存之间彼此独立且不可调节现有的工作包含以下几个不足之处
1. 资源利用不均衡:由于读写缓存大小固定,系统可能在某些工作负载下无法充分利用缓存。
例如在读密集型工作负载中,可能出现读缓存已满而写缓存大量空余的情况。
2. 缓存抖动与性能瓶颈当某一类操作如读或写消耗的缓存空间不足时数据会频繁被驱逐导致缓存抖动。这种抖动会增加IO操作降低缓存系统的性能。
3. 无法应对动态场景:在实际运用场景中,各个应用程序的访存模式会随着运行阶段的不同而发生变化。
固定大小的缓存分配方式无法灵活应对这些变化,导致性能下降。
为解决上述问题我们提出了一种新的强化学习方案OLUCB。该方案能够检测真实应用环境中读写缓存的配置情况和访存性能并根据工作负载特征对缓存配置做出实时调整有效增强了原有缓存系统的资源利用率提升读写性能。
# 二、系统架构概览
本章节介绍了我们所提出的方案架构设计以及具体模块划分显示。我们使用CMAB(contextual multi-armed bandit)对面向黑盒任务混部的资源划分问题进行建模并使用CMAB的一种广泛使用于大量场景的算法——LinUCB来设计模型。该算法选取原因如下
1. JYCache运行时内部为黑盒环境我们无法提前大量采集数据建立性能模型也无法获取任何用户提供信息。LinUCB在做决策时不需要任何事先了解工作所需要的信息上下文信息和奖励可以从跟环境交互中得到。
2. 资源划分问题可以自然地建模为MAB问题其中划分方案对应于臂一个臂的预期回报即为相应划分方案的性能。
3. LinUCB算法计算开销小满足JYCache系统隐含要求的快速响应需求。
## 第一节 系统架构图
我们将结合OLUCB方案的JYCache系统架构设计如下图所示。即相比于原JYCache系统主要修改集中在本地缓存一侧。我们将客户端缓存模块中原先彼此独立的读缓存与写缓存进行合并再分配处理而具体分配策略依赖于OLUCB根据实时运行环境下的工作负载特征所作出的决策。
![](image\OLUCB_frame.png)
## 第二节 模块划分
如架构图所示在OLUCB方案中我们采用C/S架构。其中Client运行在JYCache系统中其主要功能如下
1. 每隔一段时间收集当前读写缓冲池的大小配置信息以及统计缓存访问信息将汇总结果发送给Server端。
2. 在得到Server端发来的配置决策后调整自身读写缓冲池的比例。
Server端不间断地监听端口信息并在自身维护强化学习模型OLUCB。当接收到Client发送来的信息时Server使用该模型不断学习并作出新一轮的缓冲池配置决策发送给Client。图示如下
![](image\OLUCB_CS.png)
# 三、详细设计
本章节详细介绍了我们所设计的方案具体内容。
## 第一节 模块描述
本小节介绍了Client模块和Server模块的具体内容。
### 3.1.1 Server模块
Server模块独立运行在本机特定端口```2333```上其主要功能为和Client进行通信和维护一个强化学习模型。模型具体算法详见第二节。
### 3.1.2 Client模块
Client模块运行在JYCache系统内部单独开启一个线程用于统计、通信和执行配置。它首先会统计运行时间内写缓存和读缓存的访问情况具体表现为系统执行读/写操作的访问字节数、访问耗时和操作次数。当达到一定时间间隔后将计算出这段时间内读缓存和写缓存各自的吞吐量把经过处理的吞吐量信息作为性能指标操作次数作为上下文信息两者合并发送给Server端。
在等待一个极短的时间后Client从server端获取下一个时间段内的读写缓存配置信息。它首先根据发来的配置信息计算出该在当前读写缓存配置基础上应当增加/减少的字节数,首先进行缩池操作以保证当前缓冲池不溢出,在缩池时始终为读写缓存保留一定空间以防止系统崩溃。之后对剩下的另一个缓存池进行增池操作,最后更新两者当前的配置信息。
## 第二节 算法和逻辑
本小节具体介绍了我们所提出的强化学习算法OLUCB的运行逻辑。
### 3.2.1 以任务为导向的多层次臂结构
在原始的LinUCB算法中每一个bandit都需要维护一个参数矩阵A一个参数向量b和一个概率分布矩阵p每次bandit选择一个臂a后都要相应地更新该臂对应的A和b参数。需要探索的资源划分方案的总数是非常巨大的。为每个划分方案构造一个臂的原始 LinUCB 方法是不切实际的。在多用户资源调优场景下总的资源分配方案复杂度为O(n!)级别以三个任务240单位资源为例分配方案数达到29000+。所以为了保持合理的臂的数量OLUCB为每个任务的每一维度资源维护一个bandit将每个bandit的复杂度降为O(n)。针对多个bandit的结果依据每个bandit得出的top_k的分配进行束搜索形成完整的全局配置。
### 3.2.2 奖励设计
在我们的奖励方案中使用读缓存和写缓存各自的吞吐量作为奖励值性能指标以最大化两者的吞吐量作为最终目的。为了防止奖励值的波动过于巨大我们在Client一侧把计算所得的写入吞吐量/1024/1024后使用```std::tanh```映射到[0,1]区间。而由于对于相同长度的数据在无需从远端获取的情况下读取时间约是写入时间的4倍故读取吞吐量在原始基础上/1024/256后使用```std::tanh```映射到[0,1]区间。最终奖励为两者取平均值。
### 3.2.3 上下文特征选择
OLUCB使用读写函数的调用次数作为上下文信息由Client端负责收集统计和传给Server。上下文特征能够指导对搜索空间的搜索。对每一个臂我们维护两个上下文矩阵分别为自身上下文信息和相较于其他任务的特征差值。纳入其他任务的特征的目的是让在不同资源之间对臂的选择不独立。
### 3.2.4 臂选择策略
我们认为读写缓存资源在此处为完全隔离地被划分的,即同一块缓存空间不可能同时被读缓存/写缓存利用。在进行臂的选择时采用了beam-search算法其主要思想是依次选择任务的臂始终保持目前为止的提供更大奖励的概率最高的前L个部分选择结果最终在这些候选结果中选择最佳的。
简单而言beam-search算法以顺序的方式来生成最终决策它先选出能为
第一个任务提供更大奖励的概率最高的前 L 个臂;再在这些情况下,选择第二个任务中提供更大奖励的概率最高的前 L 个臂;直至最后一个任务;最后将所有任务选择的臂对应的概率之和最高的可行方案作为最终的决策。虽然 beam-search算法是序列决策但是其实与任务的顺序无关因为似乎先选的任务有更多的分配选择但并不意味着他们有更高的概率获得更多的资源。所有的任务都共享相同的奖励而奖励是由所有任务的表现决定的也就是每个任务应该选择的臂是所有任务的表现共同决定的。不恰当的资源分配 (例如分配过多的资源到前面的任务) 会造成负面的奖励,这将使得各 bandit 立即调整他们的选择。
### 3.2.5 超立方体采样
OLUCB是一种平衡探索与利用的算法早期阶段的探索非常关键因此其在初始运行阶段我们采用超立方采样策略进行解的随机生成。随机均匀采样可以确保OLUCB从一开始就对整个输入空间有更全面的探索能够加速模型收敛从而减少不必要的探索开销提升整体效率。
### 3.2.6 动态负载检测
在实际运行环境中各个任务在不同运行阶段的访存特征各不相同因此最佳缓存配置也各不相同。由于负载特征的变化在数值上体现为reward的变化为了使得负载特征变化后模型能识别出环境发生变化并重新开始迭代学习OLUCB维护了一个滑动窗口用于记录历史奖励。当判别到窗口的前半段均值与后半段均值差异达到一定值时OLUCB判断负载场景可能发生了变化将重新回到采样阶段开始搜索和学习寻找新的最优解。
### 3.2.7 OLUCB阶段总结
OLUCB算法迭代过程大致可分为以下三个阶段
1. 初始化采样OLUCB将在可行解空间中进行均匀采样获取一定数量的采样点在模型启动的一定轮次内将依次给出采样的解不计算预期奖励根据环境中的反馈OLUCB将以采样点中的最优解为起点开始下一阶段的迭代训练。
2. 迭代训练:训练过程中,每一轮次将根据上下文信息,历史奖励,上置信区间计算所有配置的预期奖励,选择预期奖励最高的配置。随着轮次的增长,上置信区间在预期奖励计算中所占的比重将逐渐降低,模型会逐渐倾向选择历史平均奖励更高的配置。
3. 近似收敛OLUCB维护截至当前轮次取得的最优配置在一定轮次内如未探索到更优配置则判断进行近似收敛状态。近似收敛状态下每一次选择配置时有较大的概率如90%直接选择当前已探索的最优解否则10%)继续通过计算预期奖励的方式选择配置方案,保持对环境的进一步探索。如通过后者搜索到了相较当前配置的更优解,模型将重新回到迭代训练状态。
## 第三节 接口规范
### 3.3.1 通信设计
Server和Client之间使用网络套接字socket进行通信Server端保持监听```127.0.0.1```的```2333```端口。
通信内容格式规定如下:
#### Client通信格式设计
Client负责收集和统计当前缓存相关信息具体包括当前读写缓存的大小配置信息、吞吐量以及这段时间内读写操作的频繁程度。其格式如下
```bash
"WritePool:"writeCacheSize_/fixSize; // 当前写缓存大小
"ReadPool:"readCacheSize_/fixSize;// 当前读缓存大小
"WritePool:"writeThroughput; // 写入吞吐量
"ReadPool:"readThroughput;// 读取吞吐量
"WritePool:"writeCount_; // 写入函数调用次数
"ReadPool:"readCount_;// 读取函数调用次数
```
- 当前读写缓存大小配置1~2行
- fixSize为读写缓存的分配单位大小为256M。在调节读/写缓存大小时粒度对齐到以fixSize为单位便于强化学习模型进行决策。
- CacheSize_/fixSize结果为当前读/写缓存所占大小。
- 读/写吞吐量3~4行
- 所统计的吞吐量为上一次Resize操作后到下一次Resize操作前的时间间隔内数据由Client负责计算作为强化学习模型每一步决策的reward值。
- 原始信息为s3fs内部Put和Get函数所记录的读写长度与读写时间为了避免reward值过大及不稳定计算吞吐量时会将writeThroughput在原始基础上/1024/1024后使用```std::tanh```映射到[0,1]区间。而由于对于相同长度的数据在无需从远端获取的情况下读取时间约是写入时间的4倍故readThroughput在原始基础上/1024/256后使用```std::tanh```映射到[0,1]区间。
- 上下文信息5~6行
- Count_分别为s3fs中Put()函数和Get()函数的调用次数Client将其作为附加的上下文信息发给Server便于强化学习模型调优。
- Server端获取调用次数后使用```sigmoid```函数$f(x) = \frac{1}{1 + e^{-k(x - x_0)}}$ 进行平滑处理在s3fs中设置k=0.0003, x0=13000
#### Server通信格式设计
Server端在接收Client发来的缓存配置相关信息后将使用本地的强化学习模型OLUCB对其进行分析决策出下一轮次读写缓存的大小配置并将该信息发送给Client通信内容格式如下
```bash
"WritePool" "ReadPool"
WriteSize ReadSize
```
第一行为写缓存与读缓存的名称第二行为Server端使用强化学习模型OLUCB基于所收集信息做出的最终决策。
WriteSize/ReadSize为所获得资源单位个数。
### 3.3.2 OLUCB参数说明
本小节列举出了OLUCB模型中的超参数及其意义该部分实现在LinUCBSchedule/config.py文件中
| 参数 | 解释 | 类型 |
|-----------|----------| -------|
| SAMPLE_TIMES | 初始化采样阶段的轮次数 | int |
| SAMPLE_RATIO | 采样时的放缩比例若总数100放缩比例为10则只会出现102030... | int |
|APPROXIMATE_CONVERGENCE_THRESHOLD | 多少轮次内未观察到更优结果,将进入近似收敛状态 | int |
| PROBABILITY_THRESHOLD | 利用率X%的比例选择最优值,(100-X)%探索 | int |
| WORKLOAD_CHANGE | 一段时间内性能变化超过一定幅度时,判断发生了负载变化 | float |
| LOAD_CHANGE_THRESHOLD | 超过一定轮次后reward将大致稳定将开始判断是否发生负载变化 | int|
| HISTORY_REWARD_WINDOW | 用于检查工作负载是否发生变化的滑动窗口大小 | int |
### 3.3.3 OLUCB模型方法及其参数定义
1. 构造函数__init__
```json
"__init__":{
"description":"OLUCB模型构造方法",
"parameters":{
"all_apps":{
"type":"list<str>",
"description": "每个任务以一个字符串作为标识all_apps为包含所有任务标识符的字符串列表",
"example":['writePool', 'readPool']
},
"n_cache":{
"type":"int",
"description": "资源的总数如缓存资源84G缓存以256M作为一个单位共336单位的资源",
"example":336
},
"alpha":{
"type":"float",
"description": "超参数alpha上置信区间在预期奖励计算中所占的比重",
"minimum": 0
"maximum": 1.0
"example":0.95
},
"factor_alpha":{
"type":"float",
"description": "衰减因子超参数alpha将随着轮次增加而逐渐减小",
"minimum": 0
"maximum": 1.0
"example":0.98
},
"n_features":{
"type":"int",
"description": "上下文特征的维度数",
"example":1
},
"sample":{
"type":"bool",
"description": "可选,是否进行初始化采样,或直接以环境的当前配置作为初始解",
"default":True
}
}
}
```
2. 臂选择函数select_arm
```json
"select_arm":{
"description":"选择一个新的配置",
"parameters":None,
"return_value":{
"type":"list<int>",
"description": "按照all_apps的顺序分配给每一个任务的资源单位数",
"example":[176,176]
}
}
```
3. 奖励计算函数get_now_reward
```json
"get_now_reward":{
"description":"更新上下文信息根据性能指标计算reward奖励",
"parameters":{
"performance":{
"type":"list<float>",
"description":"包含各个任务性能指标的列表",
"example":[0.85, 0.50]
},
"context_info":{
"type":"list<list<float>>",
"description":"当前环境上下文信息二维列表行数为context维度列数为任务数",
"example":[
[feature1_writePool, feature1_readPool],
[feature1_writePool, feature2_readPool],
......
[featureN_writePool, featureN_readPool]
]
}
},
"return_value":{
"type":"float",
"description":"根据performance性能指标计算出的reward,介于0-1越接近于1性能越好",
"minimum": 0,
"maximum": 1.0,
"example": 0.55,
}
"remark":"get_now_reward方法需根据实际情况调整performance和reward之间的映射关系reward越接近1性能越好",
```
4. 更新函数update
```json
"update":{
"description":"根据reward更新模型中arm的参数",
"parameters":{
"reward":{
"type":"float",
"description":"get_now_reward函数中得出的reward信息",
"minimum": 0,
"maximum": 1.0,
"example": 0.55,
}
"chosen_arm":{
"type":"dict<str->int>",
"description":"环境中当前真实的资源分配情况",
"example":{'writePool':176, 'readPool':176}
}
}
}
```
# 四、使用指南
本节介绍了如何在JYCache中实施OLUCB策略具体如下
1. JYCache路径下
```bash
# 1.从源码构建
sh build.sh
# 2.系统安装
sh install.sh
# 3.开启LinUCB服务器端不开启LinUCB调整可跳过此项
cd LinUCBSchedule && python3 server.py
```
2. JYCache/JYCache_Env路径下
```bash
# 1.开启minio
cd ./minio && sh start.sh && cd ..
# 2.修改配置文件
vim conf/newcache.conf
# 下为新增:
WriteCacheConfig.EnableThrottle=0
EnableResize = 1
EnableLinUCB = 1
# 下为修改
UseGlobalCache=0 #不开启全局缓存
# 3.启动s3fs
sh start_s3fs.sh
```
# 五、实验评估
本节重点介绍了实施OLUCB方案后缓存系统相比较于原来的读写性能提升情况包含实验环境、测试参数以及测试结果等内容。
## 第一节 实验环境
- 实验平台Linux 5.4.0-80-generic (Optane07服务器)
- 资源:本地读写缓存
- 测试工具FIO
## 第一节 参数配置
测试方法我们使用FIO测试工具生成20个大小为8G的文件进行顺序写入操作在文件全部写入完成之后对这20个文件进行随机读取操作。
FIO测试配置如下
| 参数 | 值 |
|-----------|----------|
| iodepth | 5|
| bs | 128k|
|文件总数 | 20|
| size | 8G|
运行测试时对于OLUCB的```Server端```相关参数设置如下:
| 参数 | 解释 | 值 |
|-----------|----------| -------|
| SAMPLE_TIMES | 采样轮数 | 20 |
| SAMPLE_RATIO | 采样分配单位比例每X个单位作为一个可选的可能解 | 10 |
| WORKLOAD_CHANGE | 当reward的波动超过X时判定当前工作负载发生变化 | 0.30|
| LOAD_CHANGE_THRESHOLD | 自第X轮开始识别工作负载特征判定是否变化 | 50|
| HISTORY_REWARD_WINDOW | 历史奖励维护窗口 | 50 |
| PROBABILITY_THRESHOLD | 利用率X%的比例选择最优值,(100-X)%探索 | 80 |
运行测试时对于OLUCB的```Client端```相关参数设置如下:
| 参数 | 解释 | 值 |
|-----------|----------| -------|
|fixSize | 资源分配单位,读写缓存的分配粒度| 256M|
| reserveSize | 保留空间用于避免OLUCB决策将所有缓存单一分配造成的系统崩溃| 读缓存1024M写缓存512M|
| resizeInterval_ | 单轮配置生效时间 | 5s
## 第二节 结果展示
| OP | JYCache(普通模式) | JYCache(OLUCB) |
| ------------ | ------------ | ------------ |
| 顺序写 | 680.6MiB/s | 678.9MiB/s |
| 随机读 | 810MiB/s | 1344MiB/s |
其读写缓存大小变化如下图所示:
![](/doc/image/result_LinUCB.png)
根据上图结果以及测试特征、JYCache读写流程进行分析如下
- 在前20轮学习中OLUCB模型处于采样阶段因此ReadPool/WritePool的Size变化较为剧烈reward变化较大。
- 第21轮~第80轮之间为顺序写入阶段该阶段特征为均为写入操作只有当WritePool写入率达到60%或文件完成写入时才会触发一次对于整体文件的Flush操作。受限于S3FS特性当触发Flush时需要Read文件的全部数据以构建一个完整的文件之后才能写入故而读取吞吐量不再是0reward值亦发生变化。因此统计图中显示出读写缓存配置稳定不变而reward变化剧烈的情况。
- 第81轮开始为随机读取阶段观察到在101轮前出现读写缓存配置仍然相对稳定但reward值持续走低的情况。这是因为在这一阶段OLUCB需要一定时间才能识别出工作负载的变化历史滑动窗口大小为50因此其仍然保持一段时间的原最优配置。
- 第101轮~150轮OLUCB在第101轮根据历史滑动窗口判别当前工作负载发生变化因此重新初始化模型并回到采样阶段开始进行新一轮的学习。由于总读取量至少达到160G且为随机读取因此在初期需要大量从远端存储中Download数据故而计算出的读缓存吞吐量很低奖励值映射后较低从而导致模型学习速度较慢决策出的配置并不稳定。
- 第150轮之后随着读取时间的加长模型学习出当前为读频繁阶段不断尝试给读缓存增加容量并且根据环境反馈奖励值逐渐学习到最佳分配。
## 第三节 总结叙述
根据我们的算法设计以及实验结果可以证明OLUCB方案具有以下几个特性
1. 高效性OLUCB提出了任务导向的多层次臂架构使得在线配置计算的开销很小收敛速度较快。
2. 自治性OLUCB利用LinUCB算法进行在线采样不需要任何先验知识。
3. 智能性OLUCB利用运行时信息作为上下文特征信息智能地指导在解空间的搜索减少了无用的探索能够快速收敛并找到好的解。
4. 自适应性OLUCB能够进行在线的快速学习对动态的系统变化具有良好的适用性。

View File

@ -25,7 +25,7 @@
cd LinUCBSchedule && python3 server.py
```
1. JYCache/JYCache_Env路径下
2. JYCache/JYCache_Env路径下
```bash
# 1.开启minio
cd ./minio && sh start.sh && cd ..