大连人工智能计算平台——华为昇腾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

这样我们更加确认第二次运算时超算系统的调配时间并不是占主要。对于超算系统来说,我们现在得到的假设是调度比较耗时的部分为单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

重复第二次运行情况:
代码: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

代码: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

从上面的运算表现我们可以大胆猜测,如果单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

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秒

通过最终补充的最后两个实验,我们得到的结论有之前的都不是很相符,你就是超算系统的运算性能是具备一定随机性的,系统的调度时间也是较小的,上面的运算实验之所以有较大差距或许只是系统的随机性造成的。
============================================================
posted on 2023-07-04 13:13 Angry_Panda 阅读(101) 评论(0) 收藏 举报
浙公网安备 33010602011771号