大连人工智能计算平台——华为昇腾AI平台——高性能计算HPC的单任务task的多CPU运行模式

超算是离我们平时生活比较远的一个事情,即使是对于一个计算机专业方向的学生来说,正好实验室得到了华为的超算平台的使用账号,于是就摸索了一下,不得不承认这个东西确实不是普通人能搞的明白的。

 

基本概念:

一个工作Job可以开多个副本,每个副本都是mpirun -N 所开出的,每个副本又被叫做任务task,而每个任务task又可以申请多个CPU核心和多个GPU计算资源。

 

运算代码:

复制代码
import mpi4py.MPI as MPI
import sys
import socket
import numpy as np

def func1(queue, num):
    import time
    # time.sleep(num)
    # time.sleep(1)
    x = np.random.rand(100)
    for _ in range(1000000):
        x += np.random.rand(100)
    num += np.sum(x)

    queue.put(num)


def run_queue():
    from multiprocessing import Process, Queue

    ps = 120

    queue = Queue(maxsize=200)  # the following attribute can call in anywhere

    process = [Process(target=func1, args=(queue, num)) for num in range(ps)]
    [p.start() for p in process]
    [p.join() for p in process]
    return [queue.get() for p in process]

 
comm = MPI.COMM_WORLD
comm_rank = comm.Get_rank()
comm_size = comm.Get_size()
node_name = MPI.Get_processor_name()
# node_name = socket.gethostname()
 
# point to point communication
data_send = [comm_rank]*1

comm.send(data_send,dest=(comm_rank+1)%comm_size)

res = run_queue() ###

data_recv =comm.recv(source=(comm_rank-1)%comm_size)

# print("my rank is %d, and Ireceived:" % comm_rank, data_recv, file=sys.stdout, flush=True)
# print(data_recv)

with open("/home/share/xxxxxxxxxx/home/xxxxxxxx/xxxxxxx/results/{}.txt".format(comm_rank, ), "w") as f:
    f.write("my rank is %d/%d, and node_name: %s Ireceived:" % (comm_rank, comm_size, node_name) + str(data_recv) + str(res) + "\n" )
复制代码

 

 

超算的启动命令:( -R 为task做资源申请 )

一个job开8个task,每个task申请120个CPU:

/opt/batch/cli/bin/dsub  -n task_test -A xxxxxxxxxxxx --priority 9999 --job_retry 10 --job_type hmpi -R "cpu=120;mem=128" -N 8  -eo error.txt -oo output.txt /home/share/xxxxxxxxxx/home/xxxxxxx/xxxxxxx/run_python.sh

运行时间:6分43秒

 

 

一个job开8个task,每个task申请1个CPU:

/opt/batch/cli/bin/dsub  -n task_test -A xxxxxxxxxxxx --priority 9999 --job_retry 10 --job_type hmpi -R "cpu=1;mem=128" -N 8  -eo error.txt -oo output.txt /home/share/xxxxxxxxxx/home/xxxxxxx/xxxxxxx/run_python.sh

运行时间:12分33秒

 

 

 

上面的这两个运算时间有些说不通,同样的计算task,使用120个CPU计算,单个任务用时6分43秒;然而,使用单个CPU计算单个任务,单个任务用时12分33秒,这个时间差距怎么看也不像是120个CPU和1个CPU运算时间的对比,为此我们想到一个可能,你就是这个超算平台的多CPU调度时间比较长,比较耗时,为此我们给出下面的计算:

通过这个计算可以得到一个比较合理的推断,那就是每个func1的运算时间是比较短的,而超算平台每次运算的时间都需要花费大量的调度时间,如上面的计算,每个CPU计算func1的时间如果是2.91秒,而第一次计算6分43秒的花费中其实有6分40秒左右时间是系统的调度时间,每个CPU的运算时间其实只在3秒左右,而第二次计算中使用1个CPU计算之前第一次计算中120个CPU的计算任务其运算时长自然会增加到12分33秒左右。

为此我们单独把上面的func1计算任务拿到个人笔记本上计算,使用i7-9700k cpu计算,代码:

复制代码
import numpy as np
import time

a_time = time.time()

x = np.random.rand(100)
for _ in range(1000000):
    # print(x)
    x += np.random.rand(100)
print(np.sum(x))

b_time = time.time()

print(b_time-a_time)
复制代码

用时:

可以看到上面的假设还是得到印证的,这个func1的计算任务,其用时大致在3秒左右。

 

为此我们在增加一次测试,将代码中的ps=120改为ps=1,启动命令同样为:

一个job开8个task,每个task申请120个CPU:

/opt/batch/cli/bin/dsub  -n task_test -A xxxxxxxxxxxx --priority 9999 --job_retry 10 --job_type hmpi -R "cpu=120;mem=128" -N 8  -eo error.txt -oo output.txt /home/share/xxxxxxxxxx/home/xxxxxxx/xxxxxxx/run_python.sh

运算时间:11秒

可以看到,虽然每个task的申请CPU为120个,但是每个task的计算任务只使用1个CPU,该种情况下总用时11秒。 该种情况下我们猜测虽然每个task申请120个CPU,但是实际使用了一个CPU,因此超算系统调度过程中也只为每个task进行了一个CPU的调度,为此再次运算:

代码中:ps=1

一个job开8个task,每个task申请1个CPU:

/opt/batch/cli/bin/dsub  -n task_test -A xxxxxxxxxxxx --priority 9999 --job_retry 10 --job_type hmpi -R "cpu=1;mem=128" -N 8  -eo error.txt -oo output.txt /home/share/xxxxxxxxxx/home/xxxxxxx/xxxxxxx/run_python.sh

运算时间:17秒

 

这个结果同样说明了如果task的CPU调度数越多,那么系统的调配时间越多,当然上面的计算中是认为第二次计算中的12分33秒用时中大量时间为调度时间,如果第二次中的运算用时调度时间不占主要,那么我们得到的结论并不与这里的结论现否定,给出该种情况下的计算:

 

而这个用时也是在合理范围:

 

 

 

===================================================

 

补充:

代码:ps=120   range(1000000)改为range(100000),启动命令:

/opt/batch/cli/bin/dsub  -n task_test -A xxxxxxxxxxxx --priority 9999 --job_retry 10 --job_type hmpi -R "cpu=1;mem=128" -N 8  -eo error.txt -oo output.txt /home/share/xxxxxxxxxx/home/xxxxxxx/xxxxxxx/run_python.sh

运算时间:1分26秒

 

这样我们更加确认第二次运算时超算系统的调配时间并不是占主要。对于超算系统来说,我们现在得到的假设是调度比较耗时的部分为单task的多CPU调度,就像上面的单个task的120个CPU的调配,按照这个假设第一次计算中的6分43秒中大致有6分30秒是在超算系统的调配中损耗的,而不是实际的真实计算任务所损耗的。

 

 

----------------------------------------------------------------

 

再次补充:

代码:ps=120   range(1000000)改为range(100000),启动命令:

/opt/batch/cli/bin/dsub  -n task_test -A xxxxxxxxxxxx --priority 9999 --job_retry 10 --job_type hmpi -R "cpu=120;mem=128" -N 8  -eo error.txt -oo output.txt /home/share/xxxxxxxxxx/home/xxxxxxx/xxxxxxx/run_python.sh

运算时间:46秒

 

 

重复第二次运行情况:

代码:ps=120   range(1000000),启动命令:

/opt/batch/cli/bin/dsub  -n task_test -A xxxxxxxxxxxx --priority 9999 --job_retry 10 --job_type hmpi -R "cpu=120;mem=128" -N 8  -eo error.txt -oo output.txt /home/share/xxxxxxxxxx/home/xxxxxxx/xxxxxxx/run_python.sh

运算时间:6分16秒

 

 

 

代码:ps=120   range(1000000),启动命令:

/opt/batch/cli/bin/dsub  -n task_test -A xxxxxxxxxxxx --priority 9999 --job_retry 10 --job_type hmpi -R "cpu=120;mem=128" -N 1  -eo error.txt -oo output.txt /home/share/xxxxxxxxxx/home/xxxxxxx/xxxxxxx/run_python.sh

运算时间:6分12秒

 

 

 


从上面的运算表现我们可以大胆猜测,如果单task的CPU使用数较多(如这里的120个CPU),并且单task在每个CPU上的运算时间都不是极小的(这里估计大于3秒),那么系统调度的时长会急剧增加,这里估计超算系统该种情况下的调度时长为6分钟左右。

 

 

 

 

================================================================

 

 

 

最终补充:

 

代码:ps=120   range(2000000),启动命令:

/opt/batch/cli/bin/dsub  -n task_test -A xxxxxxxxxxxx --priority 9999 --job_retry 10 --job_type hmpi -R "cpu=120;mem=128" -N 1  -eo error.txt -oo output.txt /home/share/xxxxxxxxxx/home/xxxxxxx/xxxxxxx/run_python.sh

运算时间:12分20秒

 

 
 
代码:
复制代码
import mpi4py.MPI as MPI
import sys
import socket
import numpy as np

def func1(queue, num):
    import time
    # time.sleep(num)
    time.sleep(180)
    """
    x = np.random.rand(100)
    for _ in range(2000000):
        x += np.random.rand(100)
    num += np.sum(x)
    """

    queue.put(num)


def run_queue():
    from multiprocessing import Process, Queue

    ps = 120

    queue = Queue(maxsize=200)  # the following attribute can call in anywhere

    process = [Process(target=func1, args=(queue, num)) for num in range(ps)]
    [p.start() for p in process]
    [p.join() for p in process]
    return [queue.get() for p in process]

 
comm = MPI.COMM_WORLD
comm_rank = comm.Get_rank()
comm_size = comm.Get_size()
node_name = MPI.Get_processor_name()
# node_name = socket.gethostname()
 
# point to point communication
data_send = [comm_rank]*1

comm.send(data_send,dest=(comm_rank+1)%comm_size)

res = run_queue() ###

data_recv =comm.recv(source=(comm_rank-1)%comm_size)

# print("my rank is %d, and Ireceived:" % comm_rank, data_recv, file=sys.stdout, flush=True)
# print(data_recv)
复制代码

 

启动命令:

/opt/batch/cli/bin/dsub  -n task_test -A xxxxxxxxxxxx --priority 9999 --job_retry 10 --job_type hmpi -R "cpu=120;mem=128" -N 8  -eo error.txt -oo output.txt /home/share/xxxxxxxxxx/home/xxxxxxx/xxxxxxx/run_python.sh

运算时间:3分17秒

 

 

通过最终补充的最后两个实验,我们得到的结论有之前的都不是很相符,你就是超算系统的运算性能是具备一定随机性的,系统的调度时间也是较小的,上面的运算实验之所以有较大差距或许只是系统的随机性造成的。

 
 
 
最开始的结论与最终结论的不同主要在于第一次实验和第二次实验结果的巨大差距上,之所以第二次实验单task用1个CPU开120个进程用12分多些时间,第一次实验单task用120个CPU开120个进程用6分多些时间,我个人认为这个和超算系统的调配原理有关;超算调度系统会根据job的申请去比对自己记录的系统中各主机空闲CPU数量来进行分配,第一次实验申请单task有120个CPU给120个进程,此时在超算系统的调配记录上是需要把这120个CPU的数量标记为已分配的,而第二次实验虽然申请给单task的120个进程申请1个CPU,其实超算系统的调度系统并不能具体限制这个单task所申请的CPU具体使用数,调度系统只能保证该task分配的主机至少有一个CPU空闲,但是此时所分配的主机恰好可能存在60个空闲CPU,那么该种情况下(第二次实验情况下)虽然在系统调度时向系统申请的是单task有1个CPU,但是实际运算中其实是占用了60个CPU,这也是为什么造成了第二次实验的结果不好解释的原因,这也是无形之中钻了超算调度系统的bug。

 

 

 

============================================================

 

posted on   Angry_Panda  阅读(87)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
历史上的今天:
2021-07-04 在本地主机使用anaconda3 安装MindSpore环境——教程(GPU版本)
2021-07-04 深度学习框架 MindSpore —— 华为出品的AI计算框架, docker 安装
2021-07-04 docker 容器(container)使用ssh服务登录一段时间无操作后自动断开问题解决
2021-07-04 Docker退出容器不关闭容器的方法

导航

< 2025年2月 >
26 27 28 29 30 31 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 1
2 3 4 5 6 7 8

统计

点击右上角即可分享
微信分享提示