I would like to use ZMQ in a client/server model but where each server waits until it has received 100 requests, processes them jointly, and then sends out the 100 responses back to the correct clients. The reason behind this is that the server performs a computation on GPU that is only computationally efficient when performed on batches. How can this be done with ZMQ?
Below is what I tried, which unsurprisingly raises zmq.error.ZMQError: Operation cannot be accomplished in current state because the server is trying to receive multiple requests in sequence without intermediately interleaving the recv_pyobj() calls with send_pyobj() responses.
import multiprocessing as mp
import numpy as np
import time
import zmq
def computation(inputs):
time.sleep(1) # Simulate constant GPU overhead.
results = np.zeros((len(inputs), 8))
return results
def server(port, batch=100):
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind(f'tcp://*:{port}')
while True:
inputs = np.empty((100, 64))
for i in range(batch):
inputs[i] = socket.recv_pyobj()
results = computation(inputs)
for i in range(batch):
socket.send_pyobj(results[i])
def client(ports):
context = zmq.Context()
socket = context.socket(zmq.REQ)
for port in ports:
socket.connect(f'tcp://localhost:{port}')
while True:
input_ = np.zeros(64)
socket.send_pyobj(input_)
result = socket.recv_pyobj()
if __name__ == '__main__':
num_clients = 10
num_servers = 3
ports = list(range(5550, 5550 + num_servers))
for port in ports:
mp.Process(target=server, args=(port,)).start()
for _ in range(num_clients):
mp.Process(target=client, args=(ports,)).start()