8プロセッサの64ビットWindows7マシンで100回のモデル実行を完了しようとしています。モデルの7つのインスタンスを同時に実行して、合計実行時間を短縮したいと思います(モデルの実行ごとに約9.5分)。 Pythonのマルチプロセッシングモジュールに関連するいくつかのスレッドを見てきましたが、まだ何かが足りません。
私のプロセス:
結果を比較するためにSEAWAT/MODFLOWを実行したい100の異なるパラメーターセットがあります。モデルの実行ごとにモデル入力ファイルを事前に作成し、それらを独自のディレクトリに保存しました。私がやりたいのは、すべての実現が完了するまで、一度に7つのモデルを実行することです。プロセス間の通信や結果の表示は必要ありません。これまでのところ、モデルを順番に生成することしかできませんでした。
_import os,subprocess
import multiprocessing as mp
ws = r'D:\Data\Users\jbellino\Project\stJohnsDeepening\model\xsec_a'
files = []
for f in os.listdir(ws + r'\fieldgen\reals'):
if f.endswith('.npy'):
files.append(f)
## def work(cmd):
## return subprocess.call(cmd, Shell=False)
def run(f,def_param=ws):
real = f.split('_')[2].split('.')[0]
print 'Realization %s' % real
mf2k = r'c:\modflow\mf2k.1_19\bin\mf2k.exe '
mf2k5 = r'c:\modflow\MF2005_1_8\bin\mf2005.exe '
seawatV4 = r'c:\modflow\swt_v4_00_04\exe\swt_v4.exe '
seawatV4x64 = r'c:\modflow\swt_v4_00_04\exe\swt_v4x64.exe '
exe = seawatV4x64
swt_nam = ws + r'\reals\real%s\ss\ss.nam_swt' % real
os.system( exe + swt_nam )
if __name__ == '__main__':
p = mp.Pool(processes=mp.cpu_count()-1) #-leave 1 processor available for system and other processes
tasks = range(len(files))
results = []
for f in files:
r = p.map_async(run(f), tasks, callback=results.append)
_
_if __name__ == 'main':
_によって上記のスクリプトに与えられていると感じる並列処理の欠如を修正することを期待して、_for loop
_を次のように変更しました。ただし、モデルは実行すら失敗します(Pythonエラーなし):
_if __name__ == '__main__':
p = mp.Pool(processes=mp.cpu_count()-1) #-leave 1 processor available for system and other processes
p.map_async(run,((files[f],) for f in range(len(files))))
_
すべての助けは大歓迎です!
編集2012年3月26日13:31 EST
@ J.Fで「手動プール」方式を使用します。以下のセバスチャンの答えは、外部.exeの並列実行を取得します。モデルの実現は、一度に8つのバッチで呼び出されますが、次のバッチを呼び出す前に、これらの8つの実行が完了するのを待ちません。
_from __future__ import print_function
import os,subprocess,sys
import multiprocessing as mp
from Queue import Queue
from threading import Thread
def run(f,ws):
real = f.split('_')[-1].split('.')[0]
print('Realization %s' % real)
seawatV4x64 = r'c:\modflow\swt_v4_00_04\exe\swt_v4x64.exe '
swt_nam = ws + r'\reals\real%s\ss\ss.nam_swt' % real
subprocess.check_call([seawatV4x64, swt_nam])
def worker(queue):
"""Process files from the queue."""
for args in iter(queue.get, None):
try:
run(*args)
except Exception as e: # catch exceptions to avoid exiting the
# thread prematurely
print('%r failed: %s' % (args, e,), file=sys.stderr)
def main():
# populate files
ws = r'D:\Data\Users\jbellino\Project\stJohnsDeepening\model\xsec_a'
wdir = os.path.join(ws, r'fieldgen\reals')
q = Queue()
for f in os.listdir(wdir):
if f.endswith('.npy'):
q.put_nowait((os.path.join(wdir, f), ws))
# start threads
threads = [Thread(target=worker, args=(q,)) for _ in range(8)]
for t in threads:
t.daemon = True # threads die if the program dies
t.start()
for _ in threads: q.put_nowait(None) # signal no more files
for t in threads: t.join() # wait for completion
if __name__ == '__main__':
mp.freeze_support() # optional if the program is not frozen
main()
_
エラートレースバックは利用できません。 run()
関数は、複数のファイルの場合と同様に、単一のモデル実現ファイルに対して呼び出されたときにその役割を果たします。唯一の違いは、複数のファイルがある場合、各インスタンスがすぐに閉じて1つのモデルの実行のみが終了し、スクリプトが正常に終了する(終了コード0)にもかかわらず、len(files)
回呼び出されることです。
main()
にいくつかのprintステートメントを追加すると、アクティブなスレッド数とスレッドステータスに関する情報が明らかになります(これは、スクリーンショットをより管理しやすくするための8つの実現ファイル(理論的には8つのファイルすべて)に対するテストであることに注意してください同時に実行する必要がありますが、動作はスポーンされた場所で続行され、1つを除いてすぐに終了します):
_def main():
# populate files
ws = r'D:\Data\Users\jbellino\Project\stJohnsDeepening\model\xsec_a'
wdir = os.path.join(ws, r'fieldgen\test')
q = Queue()
for f in os.listdir(wdir):
if f.endswith('.npy'):
q.put_nowait((os.path.join(wdir, f), ws))
# start threads
threads = [Thread(target=worker, args=(q,)) for _ in range(mp.cpu_count())]
for t in threads:
t.daemon = True # threads die if the program dies
t.start()
print('Active Count a',threading.activeCount())
for _ in threads:
print(_)
q.put_nowait(None) # signal no more files
for t in threads:
print(t)
t.join() # wait for completion
print('Active Count b',threading.activeCount())
_
**「_D:\\Data\\Users...
_」と表示されている行は、モデルの実行を手動で停止して完了したときにスローされるエラー情報です。モデルの実行を停止すると、残りのスレッドステータス行が報告され、スクリプトが終了します。
編集2012年3月26日16:24 EST
SEAWATは、過去にこれを行ったように同時実行を許可し、iPythonを使用してインスタンスを手動で生成し、各モデルファイルフォルダーから起動します。今回は、すべてのモデルの実行を1つの場所、つまりスクリプトが存在するディレクトリから起動します。犯人は、SEAWATが出力の一部を保存している方法にあるようです。 SEAWATを実行すると、モデルの実行に関連するファイルがすぐに作成されます。これらのファイルの1つは、モデルの実現が配置されているディレクトリではなく、スクリプトが配置されている最上位のディレクトリに保存されています。これにより、後続のスレッドが同じファイル名を同じ場所に保存できなくなります(これらのファイル名は一般的であり、各実現に固有ではないため、すべてのスレッドが保存したいと考えています)。 SEAWATウィンドウは、私が読んだり、エラーメッセージが表示されたりするのに十分な時間開いたままではありませんでした。これに気付いたのは、戻って、SEAWATからのプリントアウトを開く代わりに直接表示するiPythonを使用してコードを実行しようとしたときだけです。プログラムを実行するための新しいウィンドウ。
@ J.Fを受け入れています。このモデル実行可能問題を解決すると、彼が提供したスレッドコードによって、必要な場所に移動できる可能性が高いため、Sebastianの回答。
最終コード
Subprocess.check_callにcwd引数を追加して、SEAWATの各インスタンスを独自のディレクトリで開始します。非常に重要です。
_from __future__ import print_function
import os,subprocess,sys
import multiprocessing as mp
from Queue import Queue
from threading import Thread
import threading
def run(f,ws):
real = f.split('_')[-1].split('.')[0]
print('Realization %s' % real)
seawatV4x64 = r'c:\modflow\swt_v4_00_04\exe\swt_v4x64.exe '
cwd = ws + r'\reals\real%s\ss' % real
swt_nam = ws + r'\reals\real%s\ss\ss.nam_swt' % real
subprocess.check_call([seawatV4x64, swt_nam],cwd=cwd)
def worker(queue):
"""Process files from the queue."""
for args in iter(queue.get, None):
try:
run(*args)
except Exception as e: # catch exceptions to avoid exiting the
# thread prematurely
print('%r failed: %s' % (args, e,), file=sys.stderr)
def main():
# populate files
ws = r'D:\Data\Users\jbellino\Project\stJohnsDeepening\model\xsec_a'
wdir = os.path.join(ws, r'fieldgen\reals')
q = Queue()
for f in os.listdir(wdir):
if f.endswith('.npy'):
q.put_nowait((os.path.join(wdir, f), ws))
# start threads
threads = [Thread(target=worker, args=(q,)) for _ in range(mp.cpu_count()-1)]
for t in threads:
t.daemon = True # threads die if the program dies
t.start()
for _ in threads: q.put_nowait(None) # signal no more files
for t in threads: t.join() # wait for completion
if __name__ == '__main__':
mp.freeze_support() # optional if the program is not frozen
main()
_
Pythonコードに計算がありません。複数の外部プログラムを並行して実行する必要がある場合は、subprocess
を使用してプログラムを実行し、threading
モジュールを使用して一定数のプロセスを実行し続けるだけで十分ですが、最も単純なコードは_multiprocessing.Pool
_を使用します。
_#!/usr/bin/env python
import os
import multiprocessing as mp
def run(filename_def_param):
filename, def_param = filename_def_param # unpack arguments
... # call external program on `filename`
def safe_run(*args, **kwargs):
"""Call run(), catch exceptions."""
try: run(*args, **kwargs)
except Exception as e:
print("error: %s run(*%r, **%r)" % (e, args, kwargs))
def main():
# populate files
ws = r'D:\Data\Users\jbellino\Project\stJohnsDeepening\model\xsec_a'
workdir = os.path.join(ws, r'fieldgen\reals')
files = ((os.path.join(workdir, f), ws)
for f in os.listdir(workdir) if f.endswith('.npy'))
# start processes
pool = mp.Pool() # use all available CPUs
pool.map(safe_run, files)
if __name__=="__main__":
mp.freeze_support() # optional if the program is not frozen
main()
_
ファイルが多い場合は、pool.map()
をfor _ in pool.imap_unordered(safe_run, files): pass
に置き換えることができます。
_mutiprocessing.dummy.Pool
_と同じインターフェイスを提供する_multiprocessing.Pool
_もありますが、この場合は、プロセスの代わりにスレッドを使用します。
一部のCPUを空けておく必要はありません。実行可能ファイルを低い優先度で起動するコマンドを使用するだけです(LinuxではNice
プログラムです)。
ThreadPoolExecutor
example_concurrent.futures.ThreadPoolExecutor
_ は単純で十分ですが、 Python 2.xへのサードパーティの依存関係 (stdlibにあるのでPython 3.2)。
_#!/usr/bin/env python
import os
import concurrent.futures
def run(filename, def_param):
... # call external program on `filename`
# populate files
ws = r'D:\Data\Users\jbellino\Project\stJohnsDeepening\model\xsec_a'
wdir = os.path.join(ws, r'fieldgen\reals')
files = (os.path.join(wdir, f) for f in os.listdir(wdir) if f.endswith('.npy'))
# start threads
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
future_to_file = dict((executor.submit(run, f, ws), f) for f in files)
for future in concurrent.futures.as_completed(future_to_file):
f = future_to_file[future]
if future.exception() is not None:
print('%r generated an exception: %s' % (f, future.exception()))
# run() doesn't return anything so `future.result()` is always `None`
_
または、run()
によって発生した例外を無視した場合:
_from itertools import repeat
... # the same
# start threads
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
executor.map(run, files, repeat(ws))
# run() doesn't return anything so `map()` results can be ignored
_
subprocess
+ threading
(手動プール)ソリューション_#!/usr/bin/env python
from __future__ import print_function
import os
import subprocess
import sys
from Queue import Queue
from threading import Thread
def run(filename, def_param):
... # define exe, swt_nam
subprocess.check_call([exe, swt_nam]) # run external program
def worker(queue):
"""Process files from the queue."""
for args in iter(queue.get, None):
try:
run(*args)
except Exception as e: # catch exceptions to avoid exiting the
# thread prematurely
print('%r failed: %s' % (args, e,), file=sys.stderr)
# start threads
q = Queue()
threads = [Thread(target=worker, args=(q,)) for _ in range(8)]
for t in threads:
t.daemon = True # threads die if the program dies
t.start()
# populate files
ws = r'D:\Data\Users\jbellino\Project\stJohnsDeepening\model\xsec_a'
wdir = os.path.join(ws, r'fieldgen\reals')
for f in os.listdir(wdir):
if f.endswith('.npy'):
q.put_nowait((os.path.join(wdir, f), ws))
for _ in threads: q.put_nowait(None) # signal no more files
for t in threads: t.join() # wait for completion
_
これが、メモリ内の最小xスレッド数を維持するための私の方法です。スレッド化モジュールとマルチプロセッシングモジュールの組み合わせ。尊敬されている仲間のメンバーが上で説明したような他のテクニックには珍しいかもしれませんが、かなりの価値があるかもしれません。説明のために、一度に最低5つのWebサイトをクロールするシナリオを取り上げています。
だからここにあります:-
#importing dependencies.
from multiprocessing import Process
from threading import Thread
import threading
# Crawler function
def crawler(domain):
# define crawler technique here.
output.write(scrapeddata + "\n")
pass
次はthreadController関数です。この関数は、メインメモリへのスレッドの流れを制御します。 threadNumの「最小」制限を維持するために、スレッドをアクティブ化し続けます。 5.また、すべてのアクティブスレッド(acitveCount)が終了するまで終了しません。
最小限のthreadNum(5)startProcess関数スレッドを維持します(これらのスレッドは、60秒のタイムアウトでそれらを結合しながら、最終的にprocessListからプロセスを開始します)。 threadControllerを見つめた後、上記の5つの制限に含まれない2つのスレッドがあります。メインスレッドとthreadControllerスレッド自体。そのため、threading.activeCount()!= 2が使用されています。
def threadController():
print "Thread count before child thread starts is:-", threading.activeCount(), len(processList)
# staring first thread. This will make the activeCount=3
Thread(target = startProcess).start()
# loop while thread List is not empty OR active threads have not finished up.
while len(processList) != 0 or threading.activeCount() != 2:
if (threading.activeCount() < (threadNum + 2) and # if count of active threads are less than the Minimum AND
len(processList) != 0): # processList is not empty
Thread(target = startProcess).start() # This line would start startThreads function as a seperate thread **
startProcess関数は、別個のスレッドとして、プロセスリストからプロセスを開始します。この関数(**別のスレッドとして開始)の目的は、プロセスの親スレッドになることです。したがって、60秒のタイムアウトでそれらを結合すると、startProcessスレッドが停止して先に進みますが、threadControllerの実行は停止しません。したがって、このように、threadControllerは必要に応じて機能します。
def startProcess():
pr = processList.pop(0)
pr.start()
pr.join(60.00) # joining the thread with time out of 60 seconds as a float.
if __name__ == '__main__':
# a file holding a list of domains
domains = open("Domains.txt", "r").read().split("\n")
output = open("test.txt", "a")
processList = [] # thread list
threadNum = 5 # number of thread initiated processes to be run at one time
# making process List
for r in range(0, len(domains), 1):
domain = domains[r].strip()
p = Process(target = crawler, args = (domain,))
processList.append(p) # making a list of performer threads.
# starting the threadController as a seperate thread.
mt = Thread(target = threadController)
mt.start()
mt.join() # won't let go next until threadController thread finishes.
output.close()
print "Done"
メモリ内のスレッドの最小数を維持することに加えて、私の目的は、メモリ内のスレッドやプロセスのスタックを回避できるものを用意することでもありました。タイムアウト機能を使用してこれを行いました。入力ミスをお詫びします。
この構造がこの世界の誰にでも役立つことを願っています。よろしく、Vikas Gautam