Я провожу эксперименты с многопроцессорностью в Python. Я написал код, который требует одновременной модификации трех разных переменных (dict, float и int), общих для разных процессов. Мое понимание работы блокировки подсказывает мне, что если у меня есть три разные общие переменные, будет более эффективно назначить блокировку каждой из них. В конце концов, почему процесс 2 должен ждать изменения переменной A только потому, что процесс 1 модифицирует переменную B? Для меня имеет смысл, что если вам нужно заблокировать переменную B, то A все равно должна быть доступна другим процессам.
Я запускаю два игрушечных примера ниже, основанных на реальной программе, которую пишу, и, к моему удивлению, код работает быстрее с одной блокировкой! Одиночная блокировка: 2,1 секунды
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")
Что мне здесь не хватает? Существуют ли вычислительные затраты при переключении между блокировками, которые перевешивают любую потенциальную выгоду? Это всего лишь флаги, как такое может быть?
Примечание. Я знаю, что код работает намного быстрее при использовании одного процесса/потока, но это часть эксперимента. именно для того, чтобы понять недостатки многопроцессорной обработки.
Я провожу эксперименты с многопроцессорностью в Python. Я написал код, который требует одновременной модификации трех разных переменных (dict, float и int), общих для разных процессов. Мое понимание работы блокировки подсказывает мне, что если у меня есть три разные общие переменные, будет более эффективно назначить блокировку каждой из них. В конце концов, почему процесс 2 должен ждать изменения переменной A только потому, что процесс 1 модифицирует переменную B? Для меня имеет смысл, что если вам нужно заблокировать переменную B, то A все равно должна быть доступна другим процессам. Я запускаю два игрушечных примера ниже, основанных на реальной программе, которую пишу, и, к моему удивлению, код работает быстрее с одной блокировкой! [b]Одиночная блокировка: 2,1 секунды[/b] [code]import multiprocessing as mp import numpy as np import time
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") [/code] [b]Множественные блокировки: 2,85 секунды![/b] [code]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
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") [/code] Что мне здесь не хватает? Существуют ли вычислительные затраты при переключении между блокировками, которые перевешивают любую потенциальную выгоду? Это всего лишь флаги, как такое может быть? Примечание. Я знаю, что код работает [b]намного[/b] быстрее при использовании одного процесса/потока, но это часть эксперимента. именно для того, чтобы понять недостатки многопроцессорной обработки.
Я провожу эксперименты с многопроцессорностью в Python. Я написал код, который требует одновременной модификации трех разных переменных (dict, float и int), общих для разных процессов. Мое понимание работы блокировки подсказывает мне, что если у...
Я провожу эксперименты с многопроцессорностью в Python. Я написал код, который требует одновременной модификации трех разных переменных (dict, float и int), общих для разных процессов. Мое понимание работы блокировки подсказывает мне, что если у...
Предположим, у меня есть несколько строковых значений (динамически генерируемых) и пул потоков, и я хочу, чтобы каждое строковое значение в любой момент времени принадлежало не более чем одному потоку. Так, например, если строка foo заблокирована...
Предположим, у меня есть несколько строковых значений (динамически генерируемых) и пул потоков, и я хочу, чтобы каждое строковое значение в любой момент времени принадлежало не более чем одному потоку. Так, например, если строка foo заблокирована...
Предположим, у меня есть несколько строковых значений (динамически генерируемых) и пул потоков, и я хочу, чтобы каждое строковое значение в любой момент времени принадлежало не более чем одному потоку. Так, например, если строка foo заблокирована...