繁体   English   中英

如何使带有两个for循环的python代码运行得更快(是否存在执行Mathematica的Parallelize的python方法)?

[英]How to make the python code with two for loop run faster(Is there a python way of doing Mathematica's Parallelize)?

我对python或任何此类编程语言是全新的。 我对Mathematica有一些经验。 我有一个数学问题,尽管Mathematica用她自己的“并行化”方法解决了问题,但是在使用了所有内核之后,系统就变得筋疲力尽了! 在跑步过程中,我几乎无法使用机器。 因此,我一直在寻找一些编码替代方案,并发现python易于学习和实现。 因此,事不宜迟,让我告诉您数学问题以及我的python代码问题。 由于完整的代码太长,让我概述一下。

1.数值求解形式为y''(t)+ f(t)y(t)= 0的微分方程,以在一定范围内得出y(t),例如C <= t <= D

2.接下来,对某个所需范围的数值结果进行插值以获得函数:w(t),例如,对于A <= t <= B

3.使用w(t),求解a和b的某个范围内的另一个形式为z''(t)+ [a + b W(t)] z(t)= 0的微分方程,使用循环。

4. Deine F = 1 + sol1 [157],以生成类似{a,b,F}的列表 因此,让我给出一个原型循环,因为这需要花费大量的计算时间。

for q in np.linspace(0.0, 4.0, 100):
    for a in np.linspace(-2.0, 7.0, 100):
        print('Solving for q = {}, a = {}'.format(q,a))
        sol1 = odeint(fun, [1, 0], t, args=( a, q))[..., 0]
        print(t[157])
        F = 1 + sol1[157]                    
        f1.write("{}  {} {} \n".format(q, a, F))            
    f1.close()

现在,完成真正的循环大约需要4小时30分钟(使用w(t)的某些内置函数形式,大约需要2分钟)。 何时,我在代码中定义fun之前应用了numba / autojit (没有正确地理解它的作用和方式!),运行时间显着改善,大约需要2个小时30分钟。 此外,将两个循环作为itertools / product编写还可将运行时间仅减少约2分钟! 但是,当我让她使用全部4个核心时,Mathematica会在30分钟内完成任务。

那么,有没有办法改善python中的运行时?

为了加快python的运行速度,您可以使用以下三种选择:

  • 处理程序中的特定瓶颈(如@LutzL的注释中所建议)
  • 尝试通过使用cython将代码编译为C来加快代码速度(或使用weave或类似技术将C代码包括在内)。 由于您的情况下耗时的计算不是在python代码中正确的,而是在scipy模块中(至少我相信它们是正确的),因此这对您没有太大帮助。
  • 如您在原始问题中建议的那样实施多处理 如果您有X核心,这将使您的代码速度提高多达X(略小于)倍。 不幸的是,这在python中相当复杂。

实现多重处理-使用原始问题中的原型循环的示例

我假设您在原型代码的嵌套循环内执行的计算实际上是彼此独立的。 由于您的原型代码不完整,因此我不确定情况是否如此。 否则,它当然不起作用。 我将给出一个示例,该示例不是将您的微分方程问题用于fun函数,而是将一个具有相同签名(输入和输出变量)的原型使用。

import numpy as np
import scipy.integrate
import multiprocessing as mp

def fun(y, t, b, c):
    # replace this function with whatever function you want to work with
    #    (this one is the example function from the scipy docs for odeint)
    theta, omega = y
    dydt = [omega, -b*omega - c*np.sin(theta)]
    return dydt

#definitions of work thread and write thread functions

def run_thread(input_queue, output_queue):
    # run threads will pull tasks from the input_queue, push results into output_queue
    while True:
        try:
            queueitem = input_queue.get(block = False)
            if len(queueitem) == 3:
                a, q, t = queueitem
                sol1 = scipy.integrate.odeint(fun, [1, 0], t, args=( a, q))[..., 0]
                F = 1 + sol1[157]
                output_queue.put((q, a, F))
        except Exception as e:
            print(str(e))
            print("Queue exhausted, terminating")
            break

def write_thread(queue):    
    # write thread will pull results from output_queue, write them to outputfile.txt
    f1 = open("outputfile.txt", "w")
    while True:
        try:
            queueitem = queue.get(block = False)
            if queueitem[0] == "TERMINATE":
                f1.close()
                break
            else:
                q, a, F = queueitem                
                print("{}  {} {} \n".format(q, a, F))            
                f1.write("{}  {} {} \n".format(q, a, F))            
        except:
            # necessary since it will throw an error whenever output_queue is empty
            pass

# define time point sequence            
t = np.linspace(0, 10, 201)

# prepare input and output Queues
mpM = mp.Manager()
input_queue = mpM.Queue()
output_queue = mpM.Queue()

# prepare tasks, collect them in input_queue
for q in np.linspace(0.0, 4.0, 100):
    for a in np.linspace(-2.0, 7.0, 100):
        # Your computations as commented here will now happen in run_threads as defined above and created below
        # print('Solving for q = {}, a = {}'.format(q,a))
        # sol1 = scipy.integrate.odeint(fun, [1, 0], t, args=( a, q))[..., 0]
        # print(t[157])
        # F = 1 + sol1[157]    
        input_tupel = (a, q, t)
        input_queue.put(input_tupel)

# create threads
thread_number = mp.cpu_count()
procs_list = [mp.Process(target = run_thread , args = (input_queue, output_queue)) for i in range(thread_number)]         
write_proc = mp.Process(target = write_thread, args = (output_queue,))

# start threads
for proc in procs_list:
    proc.start()
write_proc.start()

# wait for run_threads to finish
for proc in procs_list:
    proc.join()

# terminate write_thread
output_queue.put(("TERMINATE",))
write_proc.join()

说明

  • 我们在开始计算之前定义各个问题(或更确切地说是它们的参数); 我们将它们收集在输入队列中。
  • 我们定义了一个在线程中运行的函数( run_thread )。 此函数将计算单个问题,直到输入队列中没有剩余为止。 它将结果推送到输出队列。
  • 我们启动的线程与CPU一样多。
  • 我们启动一个附加线程( write_thread ),用于从输出队列中收集结果并将其写入文件。

注意事项

  • 对于较小的问题,您可以在没有队列的情况下运行多处理。 但是,如果单个计算的数量很大,您将超过内核允许的最大线程数,之后内核将杀死您的程序。
  • 对于多处理的工作方式,不同的操作系统之间存在差异。 上面的示例将在Linux上运行(也许也可以在其他Unix之类的系统,例如Mac和BSD)上运行, 而不是在Windows上运行 原因是Windows没有fork()系统调用。 (我无权访问Windows,因此无法尝试在Windows上实现它。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM