Я запускаю два игрушечных примера ниже, основанных на реальной программе, которую пишу, и, к моему удивлению, код работает быстрее с одной блокировкой!
Одиночная блокировка: 2,1 секунды
Код: Выделить всё
import multiprocessing as mp
import numpy as np
import time
class ToyClass:
def __init__(self, shared_a, shared_b):
self.a = shared_a
self.b = shared_b
def update_a(self, key, n, lock):
with lock:
if key not in self.a:
self.a[key] = np.zeros(4)
self.a[key][n] += 1
def update_b(self, lock):
with lock:
self.b.value = max(0.1, self.b.value - 0.01)
def run_episode(toy, counter, lock):
key = np.random.randint(100)
n = np.random.randint(4)
toy.update_a(key, n, lock)
toy.update_b(lock)
with lock:
counter.value += 1
if __name__ == "__main__":
num_episodes = 1000
num_processes = 4
t0 = time.time()
with mp.Manager() as manager:
shared_a = manager.dict()
shared_b = manager.Value('d', 0)
counter = manager.Value('i', 0)
toy = ToyClass(shared_a=shared_a, shared_b=shared_b)
# Single lock
lock = manager.Lock()
pool = mp.Pool(processes=num_processes)
for _ in range(num_episodes):
pool.apply_async(run_episode, args=(toy, counter, lock))
pool.close()
pool.join()
tf = time.time()
print(f"Time to compute single lock: {tf - t0} seconds")
Код: Выделить всё
import multiprocessing as mp
import numpy as np
import time
class ToyClass: ## Same definition as for single lock
def __init__(self, shared_a, shared_b):
self.a = shared_a
self.b = shared_b
def update_a(self, key, n, lock):
with lock:
if key not in self.a:
self.a[key] = np.zeros(4)
self.a[key][n] += 1
def update_b(self, lock):
with lock:
self.b.value = max(0.1, self.b.value - 0.01)
def run_episode(toy, counter, lock_a, lock_b, lock_count):
key = np.random.randint(100)
n = np.random.randint(4)
toy.update_a(key, n, lock_a)
toy.update_b(lock_b)
with lock_count:
counter.value += 1
if __name__ == "__main__":
num_episodes = 1000
num_processes = 4
t0 = time.time()
with mp.Manager() as manager:
shared_a = manager.dict()
shared_b = manager.Value('d', 0)
counter = manager.Value('i', 0)
toy = ToyClass(shared_a=shared_a, shared_b=shared_b)
# 3 locks for 3 shared variables
lock_a = manager.Lock()
lock_b = manager.Lock()
lock_count = manager.Lock()
pool = mp.Pool(processes=num_processes)
for _ in range(num_episodes):
pool.apply_async(run_episode, args=(toy, counter, lock_a, lock_b, lock_count))
pool.close()
pool.join()
tf = time.time()
print(f"Time to compute multi-lock: {tf - t0} seconds")
Примечание. Я знаю, что код работает намного быстрее при использовании одного процесса/потока, но это часть эксперимента. именно для того, чтобы понять недостатки многопроцессорной обработки.
Подробнее здесь: https://stackoverflow.com/questions/790 ... ingle-lock