pycudaおよびpyfftパッケージを使用して、マルチ CPU およびシングル GPU計算用に次のパターンを Python で実装しようとしています。
GPU (NVIDIA CUDA を使用) を使用して FFT を実行できるように、複数のプロセス(たとえば、multiprocessing.Pool() で起動)が必要です。
ただし、次の問題があります。
あまりにも多くのプロセスを実行したり、プロセスごとにあまりにも多くの FFT を実行したりすると、スクリプト全体が停止せずに保留されたままになります(また、予定されているすべての FFT を計算することもありません)。さらなる調査から、これは GPU のメモリ制限(現在、NVIDIA GeForce GT 750M で 2048MB) が原因であると思われます。マルチプロセッシング プールが制御を取り戻すことができないようです。これを回避する方法はありますか?
各プロセスは 2048 MB 未満しか必要としないため、各プロセスが GPU の使用を予約できるキューのようなものが必要です。プロセスがコンテキストを解放すると、キュー内の次のプロセスがそれを使用し始めます。これは実行可能ですか?
あるいは、特定の時間に 1 つのプロセスだけが GPU を使用するように強制することはできますか?
これらのソリューションを個別に試しましたが、機能しません (または、正しく実装していない可能性があります)。
- proc_stream.synchronize() でストリームを同期する
- pycuda.tools.clear_context_caches() を使用して、コンテキスト キャッシュをクリアします。
- cuda.compute_mode = cuda.compute_mode.EXCLUSIVE で計算モードを変更します
注:解決策 2. はいくらかのメモリを解放するように見えますが、計算が遅くなり、問題は解決しません。たとえば、計算する fft の数を増やすと、スクリプトは同じ動作を示します。
ここにコード。簡単なタスクから始めると、ここでは各プロセスが 1 つの FFT を計算します (次に、execute() でバッチ オプションを使用して、さらに FFT を連続して実行できます)。
import multiprocessing
import pycuda.driver as cuda
import pycuda.gpuarray as gpuarray
from pycuda.tools import make_default_context
from pyfft.cuda import Plan
def main():
# generates simple matrix, (e.g. image with a signal at the center)
size = 4096
center = size/2
in_matrix = np.zeros((size, size), dtype='complex64')
in_matrix[center:center+2, center:center+2] = 10.
pool_size = 4 # integer up to multiprocessing.cpu_count()
pool = multiprocessing.Pool(processes=pool_size)
func = FuncWrapper(in_matrix, size)
nffts = 16 # total number of ffts to be computed
par = np.arange(nffts)
results = pool.map(func, par)
pool.close()
pool.join()
print results
そしてここに関数ラッパーがあります:
class FuncWrapper(object):
def __init__(self, matrix, size):
self.in_matrix = matrix
self.size = size
print("Func initialized with matrix size=%i" % size)
def __call__(self, par):
proc_id = multiprocessing.current_process().name
# take control over the GPU
cuda.init()
context = make_default_context()
device = context.get_device()
proc_stream = cuda.Stream()
# move data to GPU
# multiplication self.in_matrix*par is just to have each process computing
# different matrices
in_map_gpu = gpuarray.to_gpu(self.in_matrix*par)
# create Plan, execute FFT and get back the result from GPU
plan = Plan((self.size, self.size), dtype=np.complex64,
fast_math=False, normalize=False, wait_for_finish=True,
stream=proc_stream)
plan.execute(in_map_gpu, wait_for_finish=True)
result = in_map_gpu.get()
# free memory on GPU
del in_map_gpu
mem = np.array(cuda.mem_get_info())/1.e6
print("%s free=%f\ttot=%f" % (proc_id, mem[0], mem[1]))
# release context
context.pop()
return par
ここで、nffts=16 および pool_size=4 で、スクリプトは正しく終了し、次の出力が得られます。
Func initialized with matrix size=4096
PoolWorker-1 free=1481.019392 tot=2147.024896
PoolWorker-2 free=1331.011584 tot=2147.024896
PoolWorker-3 free=1181.003776 tot=2147.024896
PoolWorker-4 free=1030.631424 tot=2147.024896
PoolWorker-1 free=881.074176 tot=2147.024896
PoolWorker-2 free=731.746304 tot=2147.024896
PoolWorker-3 free=582.418432 tot=2147.024896
PoolWorker-4 free=433.090560 tot=2147.024896
PoolWorker-1 free=582.754304 tot=2147.024896
PoolWorker-2 free=718.946304 tot=2147.024896
PoolWorker-3 free=881.254400 tot=2147.024896
PoolWorker-4 free=1030.684672 tot=2147.024896
PoolWorker-1 free=868.028416 tot=2147.024896
PoolWorker-2 free=731.713536 tot=2147.024896
PoolWorker-3 free=582.402048 tot=2147.024896
PoolWorker-4 free=433.090560 tot=2147.024896
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
ただし、nffts=18 および pool_size=4 の場合、スクリプトは終了せず、次の出力が表示され、最後の行でスタックしたままになります。
Func initialized with matrix size=4096
PoolWorker-1 free=1416.392704 tot=2147.024896
PoolWorker-2 free=982.544384 tot=2147.024896
PoolWorker-1 free=1101.037568 tot=2147.024896
PoolWorker-2 free=682.991616 tot=2147.024896
PoolWorker-3 free=815.747072 tot=2147.024896
PoolWorker-4 free=396.918784 tot=2147.024896
PoolWorker-3 free=503.046144 tot=2147.024896
PoolWorker-4 free=397.144064 tot=2147.024896
PoolWorker-1 free=531.361792 tot=2147.024896
PoolWorker-1 free=397.246464 tot=2147.024896
PoolWorker-2 free=518.610944 tot=2147.024896
PoolWorker-2 free=397.021184 tot=2147.024896
PoolWorker-3 free=517.193728 tot=2147.024896
PoolWorker-4 free=397.021184 tot=2147.024896
PoolWorker-3 free=504.336384 tot=2147.024896
PoolWorker-4 free=149.123072 tot=2147.024896
PoolWorker-1 free=283.340800 tot=2147.024896
助けてくれて本当にありがとうございます!