如何提高计算资源——多进程并行计算(文末有一个循环计算小游戏)

      相必大家对计算机cpu的核数应该了如指掌吧,核数越多,运行速度越快,那么在linux系统上,如何利用cpu的核呢?一般Linux终端程序运行时只有一个进程,一个进程也可以理解为只用了一个核,如果你的电脑配置为10核的话,那你才用了1/10的计算资源,相当于你用高配置的电脑跑一个在低配置电脑(1-2核)需要30min运行的程序所花时间是一样的。并行计算也叫做多进程计算,指的是同时调用多个计算机内核一起工作;并发计算叫做多线程计算,是一个进程的多个程序轮换交替运行,并发计算的好处是单个程序的拖延不会影响其他程序的进行,但我们始终要清楚,并发运算好比是将一个任务拆成多份,最后还是交给一个人完成,而并行计算就是将拆分的任务交给多个人完成,那当然是并行运算这种机制的速度要快的多。

          在开始之前,我们要了解电脑的配置是几核的,终端运行下面代码

cat /proc/cpuinfo| grep "cpu cores"| uniq

一、单线程代码

代码及用途参考上一章:考虑氨基酸相对位置的蛋白特征量预测以及氨基酸突变筛选热稳定性酶

def aa_mutation2(lysin_seq):
  from functools import reduce
  from tqdm import tqdm
  import time
  global lysin_new
  lysin_new = []
  aa = ['G','A','V','L','I','F','P','S','T','H','W','C','D','E','K','Y','M','N','Q','R']
  for x in tqdm(range(len(lysin_seq) - 1)):
    for i in aa:
      lysin = list(lysin_seq)
      if lysin[x] != i:
        lysin[x] = lysin[x].replace(lysin[x],i)
        for y in range(x + 1, len(lysin_seq)):
          for ii in aa:
            if lysin[y] != ii:
              lysin[y] = lysin[y].replace(lysin[y],ii)
              lysin = str(reduce(lambda x , y: str(x) + str(y) , lysin))
              lysin_new.append(lysin)
              lysin = list(lysin)
              lysin[y] = list(lysin_seq)[y]
    time.sleep(0.01)
  
  lysin_new.append(lysin_seq)
  

二、多线程或者多进程代码

         下面为多线程或者多进程分配任务,将任务分为20份:

aa = ['G','A','V','L','I','F','P','S','T','H','W','C','D','E','K','Y','M','N','Q','R']
def aa_mutation2(aa):
  from functools import reduce
  from tqdm import tqdm
  import time
  lysin_new = []
  lysin_seq = 'MATLNDILNYAETLANQGVGADADGAYGTQCVDLPNSISINFFGKALWGNAIDLLNSAAGLGYEVVYDAVGVNPRAGAIFVMDTTYLYGHPYGHTGIVIEDSDGVTMRTIEQNIDGNADSLYVGGPARYNTRNFDGIVGWFYFPTDDTSVVFEQPEPSEPLTIESSGFHPETGTFTVEVSALNVRAEAGIGAEIVAVYGAGQEINYDGWIDNDGYIWISYIGGSRNRRYVAVGKSENGQRITDFGSFK'
  aa1 = ['G','A','V','L','I','F','P','S','T','H','W','C','D','E','K','Y','M','N','Q','R']
  for x in tqdm(range(len(lysin_seq) - 1)):
    lysin = list(lysin_seq)
    if lysin[x] != aa:
      lysin[x] = lysin[x].replace(lysin[x],aa)
      for y in range(x + 1, len(lysin_seq)):
        for ii in aa1:
          if lysin[y] != ii:
            lysin[y] = lysin[y].replace(lysin[y],ii)
            lysin = str(reduce(lambda x , y: str(x) + str(y) , lysin))
            lysin_new.append(lysin)
            lysin = list(lysin)
            lysin[y] = list(lysin_seq)[y]
    time.sleep(0.01)
  return lysin_new
  
​

拆分的方法很简单,就是把一部分循环从函数中拿出来。

     这里展示的是多线程并发计算

from multiprocessing import Pool
if __name__ == '__main__':
  p = Pool(10)#表示10个进程
  results = []
  for i in aa:
    r = p.apply_async(aa_mutation2,args=(i,))
    results.append(r)
  p.close()
  p.join()
​
#获取多进程计算的结果,必须使用get()函数
lysin = []
for i in results:
  lysin.append(i.get())

运行上述拆分的任务函数后,运行多线程并发计算,通过比较任务完成时长我们发现,并行任务所用时长是普通任务时间的1/10,仅仅用时2min就完成了原来20多分钟的任务。

三、循环计算游戏

        下面给大家出一道从网上看的题,n = 2 ,m = 12 ,将n的值-1或者*5后的新值继续-1或者*5的操作,问:多少轮后n = m,首先我们要知道每一轮都会将上一轮的数值进行-1或者*5操作,所以第k轮会产生2^k次方个数据,有点像决策树的分布。

      在没有学习编码之前,喜欢动手动脑并且有兴趣的同学肯定用笔在本子上划划、算算。那如果数值更大,或者计算更复杂呢?脑力有限,我们看看程序怎么算:

def calcaute(z,y,x):
  global n , m
  n = [z]
  num = 0
  m = []
  for i in range(x):
    num += 1
    nn = []
    for ii in range(2**i):
      nn.append(n[ii] - 1)
      nn.append(n[ii] * 5)
      if nn[-2] == y:
        print(num)
      elif nn[-1] == y:
        print(num)
    n = nn#每一轮计算值
    m.append(n)#将每一轮计算的值存入此数列中,可以用它逆推等式

运行上述函数后,调用calcaute(z,y,x),其中z,y,x分别表示n的值,m的值,以及循环数,在这里循环数是必须设的,因为这里我们没有设置终止条件,代码会一直运行,浪费计算资源。

         如计算上述n = 2,m = 12的条件,调用calcaute(2,12,10),跑10个循环,输出值为8,说明第八轮可以得到结果(如果没有值或者报错,尝试增加循环数,如x = 100),再次运行calcaute(2,12,8)进行变量赋值,然后通过索引第八轮数值12

m[7].index(12)

      输出索引号为72,那么其顺序位置应该在第八轮73位。通过观察上面函数的赋值情况,每一轮每个数会产生两个数值叠加到列表中,因此73是奇数位,12是第7轮数值通过-1得到的,因此第7轮数值应为13

m[6].index(13)

     输出索引号为36,,那么其顺序位置应该在第7轮37位,37是奇数位,13是第6轮数值通过-1得到的,因此第6轮数值应为14

m[5].index(14)

    输出索引号为18,那么其顺序位置应该在第6轮19位,19是奇数位,14是第5轮数值通过-1得到的,因此第5轮数值应为15

m[4].index(15)

    通过索引我们得到输出值为9,那么其顺序位置应该在第5轮10位,10是偶数位,15是第4轮数值通过*5得到的,因此第4轮数值应为3

m[3].index(3)

  通过索引我们得到输出值为4,那么其顺序位置应该在第4轮5位,5是奇数位,3是第3轮数值通过-1得到的,因此第3轮数值应为4

m[2].index(4)

  通过索引我们得到输出值为2,那么其顺序位置应该在第3轮3位,3是奇数位,4是第2轮数值通过-1得到的,因此第2轮数值应为5

m[1].index(5)

  通过索引我们得到输出值为1,那么其顺序位置应该在第2轮2位,2是偶数位,5是第1轮数值通过*5得到的,因此第1轮数值应为1

m[0].index(1)

  通过索引我们得到输出值为0,那么其顺序位置应该在第1轮1位,1是奇数位,1是第0轮数值通过-1得到的,因此第0轮数值应为2,第0轮也就是起始轮,其数值刚好等于n的值也就是2。结果跟我反推的结果完全一致,说明我们的程序是正确的。

    接下来我们将循环轮数的计算过程都排列出来:

def calcaute(z,y,x):
  global n , m , num2
  n = [z]
  num = 0
  m = []
  for i in range(x):
    num += 1
    nn = []
    for ii in range(2**i):
      nn.append(n[ii] - 1)
      nn.append(n[ii] * 5)
      if nn[-2] == y:
        print(num)
      elif nn[-1] == y:
        print(num)
    n = nn
    m.append(n)
  
  num1 = [y]
  a = y
  num = []
  for i in range(len(m)):
    if (m[len(m) - i - 1].index(a) + 1) % 2 == 0:
      a = int(num1[-1] / 5)
      num1.append(a)
      num.append(str(a) + '*5' + '=' + str(a * 5))
    else:
      a = int(num1[-1] + 1)
      num1.append(a)
      num.append(str(a) + '-1' + '=' + str(a - 1))
  
  num2 = []
  for i in range(len(num)):
    num2.append(num[len(num) - i - 1])
  

运行num2得到循环的计算过程:

['2-1=1', '1*5=5', '5-1=4', '4-1=3', '3*5=15', '15-1=14', '14-1=13', '13-1=12']

现在就可以推广上面的程序了,任意一个给定的n和m都能一键出结果(n和m为正整数)。更改上面计算公式(-1或者*5)也可以进一步推广其他的算法。

内容微信公众号如何提高计算资源——多进程并行计算(文末有一个循环计算小游戏)icon-default.png?t=M4ADhttps://mp.weixin.qq.com/s?__biz=Mzg2NzY2ODcxOA==&mid=2247483889&idx=1&sn=abeb9fae15ad01560711e960cd1f5410&chksm=ceb94670f9cecf660d33fb68aa8d2745036a6a2d153b5879d196425b79dc91ac569a0a43eb64&token=829275639&lang=zh_CN#rd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值