1

我有一个由递归函数组成的确定性动态编程算法,当我增加数据点(下面代码中的 x 和 s)时,我的代码需要永远(超过 5 个小时)才能给我一个输出。

我听说有一种叫做并行计算的东西multiprocessing在 python 中使用模块,但我不确定这是否能解决我的问题,如果是的话,我根本不知道。

import time
start_time = time.time()
from openpyxl import load_workbook
import pandas as pd
import numbers

wb=load_workbook(filename="data.xlsx", data_only=True)
ws=wb['Sheet1']

#for 1000 step size
x=806
s=1001
n=24

P=[0 for k in range(n)] 
for k in range(n):
    P[k]=ws.cell(row=k+2, column=2).value

X=[0 for j in range(x)] 
for j in range(x):
    X[j]=ws.cell(row=j+2, column=3).value

S=[0 for i in range(s)]
for i in range(s):
    S[i]=ws.cell(row=i+2, column=4).value

Sin=100
Sout=100

F=[[0 for j in range(x)] for i in range(s)]

#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ n=23 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
class c:
    def abc1(self):
        self.df_output1 = pd.DataFrame()
        for count, k in enumerate(range(n)):
            for i in range(s):
                for j in range(x):
                    if k==n-1:
                        if (S[i]+X[j])==Sin:
                            F[i][j]=-X[j]*P[k]
                        else:
                            F[i][j]="NA"   
        self.Fbar=list()
        self.Xbar=list()  
        for f in F:
            try:
                FFF=max([x for x in f if isinstance(x, numbers.Number)])
                XXX=X[f.index(max([x for x in f if isinstance(x, numbers.Number)]))]
                self.Fbar.append(FFF)
                self.Xbar.append(XXX)
            except ValueError:
                FFF="NA"
                self.Fbar.append(FFF)
                self.Xbar.append(FFF)
        self.df_output1["n="+str(k).format(k)] = self.Xbar 

#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 22>=n>=1 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    def abc2(self):
        list2=(list(range(n))[::-1][1:n-1])
        self.df_output2 = pd.DataFrame()
        for count, k in enumerate(list2):
            for i in range(s):
                for j in range(x):
                    try:
                        if max(S)>=(S[i]+X[j])>=min(S):
                            FFFFF=S[i]+X[j]
                            F[i][j]=-X[j]*P[k]+dict(zip(S,self.Fbar))[FFFFF]
                        if max(S)<(S[i]+X[j])<min(S):
                            F[i][j]="NA"
                    except TypeError:
                        F[i][j]="NA"         
            self.Fbar=list()
            self.Xbar=list()
            for f in F:
                try:
                    FFF=max([x for x in f if isinstance(x, numbers.Number)])
                    XXX=X[f.index(max([x for x in f if isinstance(x, numbers.Number)]))]
                    self.Fbar.append(FFF)
                    self.Xbar.append(XXX)
                except ValueError:
                    FFF="NA"
                    self.Fbar.append(FFF)
                    self.Xbar.append(FFF)
            self.df_output2["n="+str(k).format(k)] = self.Xbar                

#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ n=0 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   
    def abc3(self):
        self.df_output3 = pd.DataFrame()
        for count, k in enumerate(range(n)):
            if k==0:
                for i in range(s):
                    for j in range(x):
                        if S[i]==Sin and max(S)>=(S[i]+X[j])>=min(S):
                            FFFFF=(S[i]+X[j])
                            F[i][j]=-X[j]*P[k]+dict(zip(S,self.Fbar))[FFFFF]
                        else:
                            F[i][j]="NA"   
                self.Fbar=list()
                self.Xbar=list()  
                for f in F:
                    try:
                        FFF=max([x for x in f if isinstance(x, numbers.Number)])
                        XXX=X[f.index(max([x for x in f if isinstance(x, numbers.Number)]))]
                        self.Fbar.append(FFF)
                        self.Xbar.append(XXX)
                    except ValueError:
                        FFF="NA"
                        self.Fbar.append(FFF)
                        self.Xbar.append(FFF)
                self.df_output3["n="+str(k).format(k)] = self.Xbar

#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   

    def abc4(self):
        writer = pd.ExcelWriter('output.xlsx', engine='xlsxwriter')
        self.df_output1.to_excel(writer, sheet_name='Sheet1', startcol=0, header=True, index=False)
        self.df_output2.to_excel(writer, sheet_name='Sheet1', startcol=1, header=True, index=False)
        self.df_output3.to_excel(writer, sheet_name='Sheet1', startcol=n-1, header=True, index=False)
        writer.save()

#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

    def abc5(self):
        wb=load_workbook(filename="output.xlsx", data_only=True)
        ws=wb['Sheet1']
        X=[[0 for i in range(s)] for k in range(n)]
        Xlist=list()
        Slist=list()
        Plist=list()
        for k in range(n):
            for i in range(s):
                X[k][i]=ws.cell(column=24-k, row=i+2).value

            if k==0:
                Xstar=max([x for x in X[k] if isinstance(x, numbers.Number)])
                Sstar=Sin+Xstar
                Gain=-Xstar*P[k]
                Xlist.append(Xstar)
                Slist.append(Sstar)
                Plist.append(Gain)
            else:
                Xstar=X[k][S.index(Sstar)]
                Sstar=Sstar+Xstar
                Gain=-Xstar*P[k]
                Xlist.append(Xstar)
                Slist.append(Sstar)
                Plist.append(Gain)
        print("Profit:",sum(Plist))
foo=c()              
foo.abc1()
foo.abc2()
foo.abc3()
foo.abc4()
foo.abc5()
print("--- %s seconds ---" % (time.time() - start_time)) 

#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

如果有人可以帮助我了解如何multiprocessing在 python 中使用模块或任何其他方式来减少处理时间,那就太好了。蒂亚:)

4

1 回答 1

1

问:有什么办法可以减少处理时间?
好吧,实际上不是在纯[SERIAL]流程流中
,而且绝对不仅仅是通过启动一些multiprocessing流程

pythonmultiprocessing模块已知(并且joblib相同):

multiprocessing包提供本地和远程并发,通过使用子进程而不是线程来有效地避开全局解释器锁。

然而,就像我们宇宙中的一切一样,这是有代价的:

每个,实际上是每个multiprocessing产生的子进程都首先被实例化(在由于 O/S 导致的足够的附加延迟之后,必须处理这样的新进程及其新的 RAM 分配并将其添加到进程调度管理步骤中)作为原始python进程中存在的生态系统的---完整python副本(完整的解释器+所有它的import-ed模块+它所有的内部状态和数据结构-使用与否-)确实数量巨大发生 RAM 分配(一旦这些不再适合 RAM,这可能很快就会使平台的 O/S 不得不启动 RAM-SWAPPING,并且如果尝试填充更多子进程,则会产生毁灭性的性能影响)

到目前为止一切都很好,我们已经支付了一些(通常可以忽略不计,如果与您的 5 小时相比)时间来生成一个进程,但是100 [ns]您的交换 RAM 将花费大约100.000x更长的时间来访问内部的任何数据,而不是 RAM 访问时间“just” - pool中所有子流程的 n副本[CONCURRENT]

正如您所确认的那样,您的处理是一个纯粹的[SERIAL],一个接一个的步骤,因此如果使用最好的并发工具并且不犯任何并发采用策略错误,那么“更快”完成工作的机会为零。

因此,因此,您为许多子流程实例支付了某种“可接受的”成本,此外,由于+10k x多次复制相同数据的访问时间变慢的因素,您还要支付破坏性能的极端成本(您现在以一种又一种方式访问​​)并且已经支付了所有这些费用,使用所有multiprocessing工具没有任何好处,但仍然是纯[SERIAL]流程?

这没有任何意义。

你只需付出比你收到的更多的钱来换取这样做。

Amdahl 的法律重新制定(此处)中有更多关于并行附加成本经济性的详细信息。

如果您可以提出一种有效的算法重构,则可能有机会从引入的一些新可能的并发中受益,因为可能会更有效地重新安排流程,但不是在纯[SERIAL]步骤序列中(one-after -又一个又一个-...)

最后,5小时没问题。存在数值问题,即使在有效并行化之后也需要数天甚至数周才能得到处理,因此列表迭代器有序转换的顺序排序序列可能会受益,但可以受益于cpython-compilation 或numba-LLVM-compilation(如果列表重新-factored 以便让它们在numba.jit()-compiler 功能限制范围内)

问:有什么方法可以在 python 中以任何其他方式减少处理时间?

哦,是的,有。分析代码执行流程和调试器(在这里,在您将焦点集中到.abc3()Class-method 之后,相同的规则适用于上面所有其他类似编码的方法,即使是肉眼也可以)可以告诉您,您将大部分时间花在了哪里代码执行时间以及重构可能最有帮助的地方,以提高计算性能的吞吐量。

值得注意的是,原始代码主要存在缺陷,在多个地方进行了相同的相当昂贵的操作块之后,连续执行无效甚至重复的操作,并且在三重地狱中非常糟糕和低效,大约有 806.000.000 循环重复(以最慢的 python循环而for闻名)

############################################################################
# RE-FACTORIN REMARKS - REF. APPROXIMATE SCALES OF MAGNITUDES ( RAM ALLOCS + CPU PROCESSING ):
#                                               OF WASTED TIME & RESOURCES
# x =  806
# s = 1001
# n =   24
# P ~ [0:23]
# X ~ [0:806]
# S ~ [0:1000]
# F ~ [0:1000,0:805]
def abc3_faster( self ):
    """                                      __doc__ [DOC-ME]..."""
    self.df_output3 = pd.DataFrame()
    #--------------------------------------- 1x -------- k == 0
    max__S = max( S )           #----------- 1x static min( S[:1000] ) !806k times re-eval'd
    min__S = min( S )           #----------- 1x static min( S[:1000] ) !806k times re-eval'd
    all_NA = [ "NA" for j in range( x ) ] #- 1x static [:806]          !many times re-eval'd
    DictZIP= dict( zip( S, self.Fbar ) ) #-- 1x static [:1000]         ! 19M times re-eval'd
    ####################################################################################################
    # STATIC                                                                                           
    # CASE: k==0 --------------------------------------------------------------------------------<-*-<-*
    for        i in range( s ): #---------------LOOP 1001x                                         ^   ^
        pass;F[i] = all_NA  #---------------pre-fill------806x per-item looping avoided            ^   ^
        if ( S[i] == Sin ): #-------------------fill-if------------item conditions met             ^   ^
            for j in range( x ): #--------------LOOP 1001x806x                                     ^   ^
                FFFFF   = ( S[i] + X[j] ) #-------- PRE-COMPUTE / REUSE ..................         ^   ^
                if ( #                S[i] == Sin ~ PRE-TESTED ABOVE                      :        ^   ^
                   #nd max( S )  >= ( S[i] + X[j] ) >= min( S )                           :        ^   ^
                       max__S    >= ( FFFFF       ) >= min__S # MOST OF THE TIME IT WILL..:        ^   ^
                       ):                                                                 :        ^   ^
                    #FFFF   = ( S[i] + X[j] )     # PRE-COMPUTED ABOVE                    :        ^   ^
                    #[i][j] = (       -X[j] * P[k]# ENFORCED BY if k == 0:----------------:--------!   ^
                    F[i][j] = (       -X[j] * P[0]#    thus        k == 0 <---------------:--------!   ^
                              # dict( zip( S, self.Fbar )                                 :        v   ^
                              #       )[FFFFF]                                            :        v   ^
                              + DictZIP[FFFFF]    # <--------------------------------------        v   ^
                                )                 #                                                v   ^
                #-------------------------pre-fill-ed already above                                v   ^
                #lse:                     ### before [j]-LOOP-ing                                  v   ^
                #   F[i][j] = "NA"                                                                 v   ^
    #--------------------------------------------------------------------------------------------<-v   ^
    #or count, k in enumerate( range(    n ) ): #----- LOOP 24x ----- NEVER USED VALUES------------v---^
    #   if k==0:                                #----- LOOP 23x NOP-- k==0 DONE ABOVE <----------<-*---^
    #      ....THIS_BODY_OF_THE_ONLY_MEANINGFUL_WOK_REFACTORED.........................................*
    #-----------------------------------------------------------                                       v
    self.Fbar = list()                # blank-ed by empty list()                                       v
    self.Xbar = list()                # blank-ed by empty list()                                       v
    #-------------------------------------------------------------------------------------#            v
    for f in F: #-------------------------------LOOP-1001x                                #            v
        try:                                                                              #            v
            FFF =            max( [ x for x in f if isinstance( x, numbers.Number ) ] )   #            v
            #XX = X[f.index( max( [ x for x in f if isinstance( x, numbers.Number ) ] ) ) ]            v
            #----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----            v
            #                A STRAIGHT DUPLICATE OF THE SAME WORK? WASTES TIME & RESOURCES            v
            ###############################################################################            v
            self.Fbar.append( FFF )                                                       #            v
            #elf.Xbar.append( XXX )                                                       #            v
            self.Xbar.append( X[f.index( FFF )] )                                         #            v
        except ValueError:                                                                #            v
            self.Fbar.append( "NA" )                                                      #            v
            self.Xbar.append( "NA" )                                                      #            v
            #                                                                             #            v
    #elf.df_output3["n="+str(k).format(k)] = self.Xbar # str( 0 ).format( 0 ) # a void operation       v
    self.df_output3["n=0"] = self.Xbar # <-------------------------------------------------------------v

奖金 - 总结通知:

最好避免使用类的整个基础结构及其各自的方法,如果只是执行它们各自调用Class的纯序列。[SERIAL]foo.m1();foo.m2();foo.m3();foo.m4();foo.m5()

您支付费用却没有收到任何回报。

接下来,学习并将整个纯[SERIAL]管道处理重构为numpy向量/矩阵和更智能的内置numpy.NaN处理方式。避免简单的list()基于 - 的数字表示将进一步提高可实现的性能,而+4x不仅仅是智能numpy矢量化的一个因素,最后但并非最不重要的是,通过 - 编译numpy加速是安全的numba,而list()-s 可能会导致问题,被某些人拒绝较早的numbaLLVM 预处理器版本

没有其他魔法可以使用。

于 2019-07-07T05:12:53.717 回答