Right now, there's some overhead in gradient computations: deepcopy needs to be called on every computation to make sure gradients aren't shared between workers. This about doubles the time for a gradient computation (128 gradients w/ Wide-ResNet 16-4 and CIFAR10 dataset on a GPU).
Right now, we're using Client(processes=False) to avoid the need for serialization (we're using the GPU, so there's no need to send objects between CPU cores). But one complication of this is that the same memory address is broadcasted to each worker, so they all modify the same object (which leads to a race condition). We call deepcopy to address this immediate need.
How can the call to deepcopy be avoided?
Timing script for serialization
# [ins] In [30]: %timeit deepcopy(m)
# 35.4 ms ± 237 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
# [ins] In [31]: %timeit deepcopy(x)
# 92.4 ms ± 1.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# [ins] In [34]: %timeit deepcopy(y)
# 97.1 ms ± 2.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
from torchvision.models import resnet34
import pickle
from time import perf_counter as time
from io import BytesIO
import numpy as np
def np_pickle(x):
start = time()
y = pickle.loads(pickle.dumps(x))
end = time()
return end - start
def np_pickle2(x):
start = time()
with BytesIO() as f:
np.save(f, x)
y = np.load(f)
end = time()
return end - start
def torch_model(m):
start = time()
m2 = pickle.loads(pickle.dumps(m))
end = time()
return end - start
m = resnet34()
nparams = sum(p.nelement() for p in m.parameters())
x = np.random.uniform(size=nparams)
assert len(x) >= 1e6
n_runs = 30
times = []
for _ in range(n_runs):
times.append(np_pickle(x))
ms = int(min(times) * 1000)
print(f"Serializing numpy array w/ pickle took at least {ms}ms") # 265ms
times = []
for _ in range(n_runs):
times.append(np_pickle(x))
ms = int(min(times) * 1000)
print(f"Serializing numpy array took at least {ms}ms") # 275ms
times = []
for _ in range(n_runs):
times.append(torch_model(m))
ms = int(min(times) * 1000)
print(f"Serializing resnet34 took at least {ms}ms") # 245ms
Right now, there's some overhead in gradient computations:
deepcopyneeds to be called on every computation to make sure gradients aren't shared between workers. This about doubles the time for a gradient computation (128 gradients w/ Wide-ResNet 16-4 and CIFAR10 dataset on a GPU).Right now, we're using
Client(processes=False)to avoid the need for serialization (we're using the GPU, so there's no need to send objects between CPU cores). But one complication of this is that the same memory address is broadcasted to each worker, so they all modify the same object (which leads to a race condition). We calldeepcopyto address this immediate need.How can the call to
deepcopybe avoided?Timing script for serialization