实验选题
本次实验选择与Ray相关的部署、测试、分析、评价。将使用矩阵乘法的并行计算作为测试任务。
计算类性能指标
- 执行时间:任务完成所需的实际时间。执行时间越短,任务执行速度越快,性能越好。
- 吞吐量:任务在单位时间内完成的工作量。通常以任务数量、操作次数、计算量等指标来衡量。较高的吞吐量表示任务能够以较快的速度处理更多的工作
- 并行度:任务在并行环境中利用的计算资源数量。并行度较高表示任务能够有效地利用多核处理器或分布式计算资源,从而提高性能。
- 资源利用率:任务对计算资源的利用效率。资源利用率高表示任务能够充分利用计算资源,避免资源闲置或浪费。
- 扩展性:任务在增加计算资源(如CPU核心、节点数等)时,性能能否线性或接近线性地提升。较好的扩展性表示任务能够有效地适应更大规模的计算环境。
选取执行时间与吞吐量作为测试指标。
执行时间越短,任务执行速度越快,说明计算性能越好,而吞吐量能反映系统在处理大量任务时的总体速率,是并行运算的重要指标。所以选择这两个指标来衡量并行计算性能。
假设一个 n × n n \times n n×n大小的矩阵,使用的吞吐量计算公式为 n × n 任务运行时间 × 1 0 − 4 \frac{n\times n}{任务运行时间} \times 10^{-4} 任务运行时间n×n×10−4
ray的部署及测试
ray的单机版部署
安装pip
cd #切换到根目录
sudo apt install pip
安装pytest-runner
pip install pytest-runner
安装ray(可能会失败,可以多尝试几次)
sudo apt-get update
sudo pip3 install -U ray
安装ray[default]
sudo pip3 install 'ray[default]'
至此ray的单机部署完成
ray的集群部署
运行以下指令创建head节点
ray start --head --port=6379 --include-dashboard=true --dashboard-host=0.0.0.0 --dashboard-port=8265
出现信息中含有:
这说明要创建从节点需要运行
ray start --address='172.31.175.228:6379'
查看Ray Dashboard界面
可以在web看到当前Ray Dashboard界面,其中VM6629-CS2023为刚创建的头节点,VM6549-qiu是刚建立的从节点
测试ray集群是否可用
尝试python代码
import ray
ray.init(address = "172.31.175.228:6379")
@ray.remote
def hello():
return "Hello"
@ray.remote
def world():
return "world!"
@ray.remote
def hello_world(a, b):
return a + " " + b
a_id = hello.remote()
b_id = world.remote()
c_id = hello_world.remote(a_id, b_id)
hello = ray.get(c_id)
print(hello)
运行:
python hello_world.py
结果如下:
说明ray的集群部署成功
基于Docker的Ray部署
安装Docker
sudo apt install docker.io
使用如下命令下载Docker镜像:
sudo docker pull rayproject/ray
在Docker中运行rayproject/ray
sudo docker run -it rayproject/ray
单机版ray的性能测试及优化
下面进行任务执行时间的测试及优化
选取矩阵乘法作为测试程序,在没有ray的情况下,测试程序为程序1:
import numpy as np
import time
def matrix_multiplication(matrix_a, matrix_b):
# 获取矩阵 A 和 B 的维度
rows_a, cols_a = matrix_a.shape
rows_b, cols_b = matrix_b.shape
# 确保矩阵可以相乘
assert cols_a == rows_b
# 创建一个用于存储结果的矩阵
result = np.zeros((rows_a, cols_b))
# 计算矩阵乘积
for i in range(rows_a):
for j in range(cols_b):
for k in range(cols_a):
result[i, j] += matrix_a[i, k] * matrix_b[k, j]
return result
def generate_random_square_matrix(n):
# 生成 n x n 的随机方阵
matrix = np.random.randint(0, 1000, size=(n, n))
return matrix
n = 100
matrix1 = generate_random_square_matrix(n)
matrix2 = generate_random_square_matrix(n)
tic = time.time()
matrix_multiplication(matrix1, matrix1)
duration = (time.time() - tic)
print("duration = "+ str(duration))
运行结果:
使用单机版ray,程序修改为并行计算的结构,程序2的代码如下:
import ray
import numpy as np
import time
ray.init()
@ray.remote
def multiply_row_column(row, column):
# 计算矩阵的一行和一列的乘积
return np.dot(row, column)
def parallel_matrix_multiplication(matrix_a, matrix_b):
# 获取矩阵的维度
rows_a, cols_a = matrix_a.shape
rows_b, cols_b = matrix_b.shape
# 确保矩阵可以相乘
assert cols_a == rows_b
# 创建一个存储结果的矩阵
result = np.zeros((rows_a, cols_b))
# 并行计算矩阵乘积
for i in range(rows_a):
for j in range(cols_b):
# 提取矩阵的一行和一列
row = matrix_a[i]
column = matrix_b[:, j]
# 在 Ray 上并行计算乘积
result[i, j] = ray.get(multiply_row_column.remote(row, column))
return result
def generate_random_square_matrix(n):
# 生成 n x n 的随机方阵
matrix = np.random.randint(0, 1000, size=(n, n))
return matrix
n = 100
matrix1 = generate_random_square_matrix(n)
matrix2 = generate_random_square_matrix(n)
tic = time.time()
parallel_matrix_multiplication(matrix1, matrix1)
duration = (time.time() - tic)
print("duration = "+ str(duration))
运行结果:
结果令人意想不到,使用并行计算之后,程序的运行时间暴涨,通过查阅资料,得知创建任务也需要一定的开销,程序2会创建 n 2 n^2 n2个任务,当 n = 100 n=100 n=100时任务的数量就是10000个,但每个任务实际上只执行了100个乘法运算(加法忽略),这说明任务创建的开销甚至比任务执行的开销还要大,导致了程序2的执行时间要远高于程序1。将调整,将并行计算的任务修改为程序3:
import ray
import numpy as np
import time
import string
#ray.init(object_store_memory = 1*1024*1024*1024, num_cpus = 4)
ray.init()
@ray.remote
def multiply_row(row, matrix_b):
result = np.dot(row, matrix_b)
return result
def parallel_matrix_multiply(matrix_a, matrix_b):
# 将矩阵B转置,以便按行并行计算
transposed_b = np.transpose(matrix_b)
results = []
for row_a in matrix_a:
results.append(multiply_row.remote(row_a, transposed_b))
# 获取并汇总所有并行计算的结果
results = ray.get(results)
# 将结果组合成最终的乘积矩阵
result_matrix = np.vstack(results)
return result_matrix
def generate_random_square_matrix(n):
# 生成 n x n 的随机方阵
matrix = np.random.randint(0, 1000, size=(n, n))
return matrix
n = 100
matrix1 = generate_random_square_matrix(n)
matrix2 = generate_random_square_matrix(n)
tic = time.time()
parallel_matrix_multiply(matrix1, matrix1)
duration = (time.time() - tic)
print("duration = "+ str(duration))
运行结果如下
相较于程序2,程序3只会创建100个任务,且每个任务会执行10000个乘法运算,任务创建开销远小于任务开销,效果也是非常显著,程序3的运行速度是程序2的十几倍,程序1的2倍以上。并行计算任务的性能得到了极大的优化(远远大于20%)。
使用程序3进行任务运行时间和吞吐量的测试和分析
调整矩阵大小,任务执行时间和吞吐量如下:
矩阵大小 | 100 | 200 | 400 | 800 | 1000 | 1200 |
---|---|---|---|---|---|---|
执行时间(s) | 0.186 | 0.366 | 0.779 | 2.05 | 3.21 | 4.61 |
吞吐量 | 5.38 | 11.90 | 20.53 | 31.21 | 31.15 | 31.23 |
由数据可知,任务执行时间随着矩阵大小的增加而持续增加,吞吐量在矩阵较小时随矩阵大小的增加而增加,但当矩阵大于800时,吞吐量趋于恒定。分析其原因,可将程序运行时间分为矩阵运算时间和其他时间,矩阵较小时其他时间不可忽略,随着矩阵大小的增加,其他时间所占的比重越来越小,所以吞吐量增加,当矩阵大到一定程度时,其他时间可以忽略不计,吞吐量趋于恒定。
分布式ray性能测试
ray集群部署如下:
使用程序3进行任务运行时间和吞吐量的测试和分析
矩阵大小 | 100 | 200 | 400 | 500 | 600 | 700 |
---|---|---|---|---|---|---|
执行时间(s) | 1.63 | 2.52 | 3.87 | 5.07 | 7.00 | 9.66 |
吞吐量 | 0.61 | 1.58 | 2.16 | 4.93 | 5.14 | 5.07 |
结论与单机测试结论相同,矩阵大于500时,吞吐量趋于恒定。
注:由于单机和分布式采用了不同的虚拟机,所以他们之间执行时间没有可比性。
基于Docker的分布式ray性能测试
使用如下命令启动head节点:
sudo docker run -it rayproject/ray
在Docker的dash中创建头节点
ray start --head --port=6379 --include-dashboard=true --dashboard-host=0.0.0.0 --dashboard-port=8265
结果如下:
打开另一个Docker创建从节点
ray start --address='172.17.0.2:6379'
查看节点状态:
ray status
显示有两个节点,说明基于Docker的Ray部署成功。
单节点程序运行时间( 800 × 800 800 \times 800 800×800)
使用程序3进行任务运行时间和吞吐量的测试和分析(两个节点)
矩阵大小 | 100 | 200 | 400 | 600 | 800 | 1000 |
---|---|---|---|---|---|---|
执行时间(s) | 2.16 | 2.58 | 3.63 | 6.03 | 10.80 | 17.08 |
吞吐量 | 0.463 | 1.55 | 4.40 | 5.97 | 5.92 | 5.85 |
结论与单机测试结论相同,矩阵大于600时,吞吐量趋于恒定。