def __init__( self, host, port=9091, port_end=9199, web_port=0, timeout_client=600, timeout_server=600, tracker_addr=None, index_page=None, resource_files=None, ): self.proc = PopenWorker() # send the function self.proc.send( _popen_start_proxy_server, [ host, port, port_end, web_port, timeout_client, timeout_server, tracker_addr, index_page, resource_files, ], ) # receive the port self.port = self.proc.recv() self.host = host
def test_popen_initializer(): initargs = [1, 2, 3] proc = PopenWorker(initializer=initializer, initargs=initargs) proc.send(after_initializer) test_global_state_1, test_global_state_2, test_global_state_3 = proc.recv() assert test_global_state_1 == initargs[0] assert test_global_state_2 == initargs[1] assert test_global_state_3 == initargs[2]
def test_popen_worker_reuses(): proc = PopenWorker(maximum_uses=None) proc.send(os.getpid) initial_pid = proc.recv() proc.send(os.getpid) assert proc.recv() == initial_pid
def __init__(self, host="0.0.0.0", port=9190, port_end=9199, silent=False): if silent: logger.setLevel(logging.WARN) self.proc = PopenWorker() # send the function self.proc.send( _popen_start_tracker_server, [ host, port, port_end, silent, ], ) # receive the port self.port, self.stop_key = self.proc.recv() self.host = host
def __init__( self, host="0.0.0.0", port=9091, port_end=9199, is_proxy=False, tracker_addr=None, key="", load_library=None, custom_addr=None, silent=False, no_fork=False, server_init_callback=None, ): try: if _ffi_api.ServerLoop is None: raise RuntimeError("Please compile with USE_RPC=1") except NameError: raise RuntimeError("Please compile with USE_RPC=1") self.proc = PopenWorker() # send the function self.proc.send( _popen_start_rpc_server, [ host, port, port_end, is_proxy, tracker_addr, key, load_library, custom_addr, silent, no_fork, server_init_callback, ], ) # receive the port self.port = self.proc.recv() self.host = host
def wrapper_for_call_function_with_timeout(timeout, func, args=(), kwargs=None): """Wrapper for call_func_with_timeout.""" def wrapper(*_args, **_kwargs): """ This wrapper is needed because the cloudpicle cannot serialize objects that contain pointers (RPCSession) """ func(*_args, **_kwargs) return StatusKind.COMPLETE worker = PopenWorker() ret = call_func_with_timeout(worker, timeout=timeout, func=wrapper, args=args, kwargs=kwargs) if isinstance(ret, Exception): raise ret return ret
def local_run( inputs, build_results, timeout=10, number=3, repeat=1, min_repeat_ms=0, cooldown_interval=0, enable_cpu_cache_flush=False, verbose=1, ): """ Run function of LocalRunner to test the performance of the input BuildResults. Parameters ---------- inputs : List[MeasureInput] The MeasureInputs to be measured. build_results : List[BuildResult] The BuildResults to be measured. timeout : int = 10 The timeout limit (in second) for each run. This is used in a wrapper of the multiprocessing.Process.join(). number : int = 3 The number of times to run the generated code for taking average. We call these runs as one `repeat` of measurement. repeat : int = 1 The number of times to repeat the measurement. In total, the generated code will be run (1 + number x repeat) times, where the first "1" is warm up and will be discarded. The returned result contains `repeat` costs, each of which is an average of `number` costs. min_repeat_ms : int = 0 The minimum duration of one `repeat` in milliseconds. By default, one `repeat` contains `number` runs. If this parameter is set, the parameters `number` will be dynamically adjusted to meet the minimum duration requirement of one `repeat`. i.e., When the run time of one `repeat` falls below this time, the `number` parameter will be automatically increased. cooldown_interval : float = 0.0 The cool down interval between two measurements in seconds. enable_cpu_cache_flush: bool = False Whether to flush cache on CPU between repeated measurements. Flushing cache can make the measured latency of one operator closer to its actual latency during end-to-end inference. To make this option effective, the argument `number` should also be set to 1. This is only has effect on CPU task. verbose: int = 1 Verbosity level. 0 for silent, 1 to output information during program measuring. Returns ------- res : List[MeasureResult] The measure results of these MeasureInputs. """ measure_results = [] assert len(inputs) == len( build_results), "Measure input size should be equal to build results" worker = PopenWorker() for inp, build_res in zip(inputs, build_results): if build_res.error_no != 0: res = ( (MAX_FLOAT, ), build_res.error_no, build_res.error_msg, build_res.time_cost, time.time(), ) else: args = prepare_runner_args(inp, build_res) res = call_func_with_timeout( worker, timeout, _timed_eval_func, args=( inp.serialize(), build_res, args, number, repeat, min_repeat_ms, cooldown_interval, enable_cpu_cache_flush, verbose, ), ) if isinstance(res, TimeoutError): if verbose >= 1: print("*T", end="", flush=True) # Run timeout res = ( (MAX_FLOAT, ), MeasureErrorNo.RUN_TIMEOUT, None, build_res.time_cost + timeout, time.time(), ) elif isinstance(res, Exception): if verbose >= 1: print("*E", end="", flush=True) # Run error res = ( (MAX_FLOAT, ), MeasureErrorNo.RUNTIME_DEVICE, str(res), build_res.time_cost + timeout, time.time(), ) measure_results.append(MeasureResult(*res)) if verbose >= 1: print("", flush=True) return measure_results
def test_popen_ffi(): proc = PopenWorker(register_ffi) # call python function via ffi initargs = [0] proc.send(call_py_ffi, initargs) assert proc.recv() == initargs[0] # call cpp function via ffi initargs = [1] proc.send(call_cpp_ffi, initargs) assert proc.recv() == initargs[0] # call python function from cpp function via ffi initargs = [2] proc.send(call_cpp_py_ffi, initargs) assert proc.recv() == initargs[0]
def test_popen_worker(): proc = PopenWorker() with pytest.raises(TimeoutError): proc.send(identity_after, [1, 100], timeout=0.01) proc.recv() with pytest.raises(ChildProcessError): proc.send(terminate_self) proc.recv() proc.send(identity_after, [2, 0]) assert proc.recv() == 2 proc.send(identity_after, [4, 0.0001]) assert proc.recv() == 4
class Tracker(object): """Start RPC tracker on a separate process. Python implementation based on PopenWorker. Parameters ---------- host : str The host url of the server. port : int The TCP port to be bind to port_end : int, optional The end TCP port to search silent: bool, optional Whether run in silent mode """ def __init__(self, host="0.0.0.0", port=9190, port_end=9199, silent=False): if silent: logger.setLevel(logging.WARN) self.proc = PopenWorker() # send the function self.proc.send( _popen_start_tracker_server, [ host, port, port_end, silent, ], ) # receive the port self.port, self.stop_key = self.proc.recv() self.host = host def _stop_tracker(self): sock = socket.socket(base.get_addr_family((self.host, self.port)), socket.SOCK_STREAM) sock.connect(("127.0.0.1", self.port)) sock.sendall(struct.pack("<i", base.RPC_TRACKER_MAGIC)) magic = struct.unpack("<i", base.recvall(sock, 4))[0] assert magic == base.RPC_TRACKER_MAGIC base.sendjson(sock, [TrackerCode.STOP, self.stop_key]) assert base.recvjson(sock) == TrackerCode.SUCCESS sock.close() def terminate(self): """Terminate the server process""" if self.proc: if self.proc.is_alive(): self._stop_tracker() self.proc.join(0.1) if self.proc.is_alive(): logger.info("Terminating Tracker Server...") self.proc.kill() self.proc = None def __del__(self): try: self.terminate() except TypeError: pass
class Server(object): """Start RPC server on a separate process. This is a simple python implementation based on multi-processing. It is also possible to implement a similar C based server with TVM runtime which does not depend on the python. Parameters ---------- host : str The host url of the server. port : int The port to be bind to port_end : int, optional The end port to search is_proxy : bool, optional Whether the address specified is a proxy. If this is true, the host and port actually corresponds to the address of the proxy server. tracker_addr: Tuple (str, int) , optional The address of RPC Tracker in tuple(host, ip) format. If is not None, the server will register itself to the tracker. key : str, optional The key used to identify the device type in tracker. load_library : str, optional List of additional libraries to be loaded during execution. custom_addr: str, optional Custom IP Address to Report to RPC Tracker silent: bool, optional Whether run this server in silent mode. no_fork: bool, optional Whether forbid fork in multiprocessing. """ def __init__( self, host="0.0.0.0", port=9091, port_end=9199, is_proxy=False, tracker_addr=None, key="", load_library=None, custom_addr=None, silent=False, no_fork=False, ): try: if _ffi_api.ServerLoop is None: raise RuntimeError("Please compile with USE_RPC=1") except NameError: raise RuntimeError("Please compile with USE_RPC=1") self.proc = PopenWorker() # send the function self.proc.send( _popen_start_rpc_server, [ host, port, port_end, is_proxy, tracker_addr, key, load_library, custom_addr, silent, no_fork, ], ) # receive the port self.port = self.proc.recv() self.host = host def terminate(self): """Terminate the server process""" if self.proc: self.proc.kill() self.proc = None def __del__(self): self.terminate()
class Proxy(object): """Start RPC proxy server on a seperate process. Python implementation based on PopenWorker. Parameters ---------- host : str The host url of the server. port : int The TCP port to be bind to port_end : int, optional The end TCP port to search web_port : int, optional The http/websocket port of the server. timeout_client : float, optional Timeout of client until it sees a matching connection. timeout_server : float, optional Timeout of server until it sees a matching connection. tracker_addr: Tuple (str, int) , optional The address of RPC Tracker in tuple (host, ip) format. If is not None, the server will register itself to the tracker. index_page : str, optional Path to an index page that can be used to display at proxy index. resource_files : str, optional Path to local resources that can be included in the http request """ def __init__( self, host, port=9091, port_end=9199, web_port=0, timeout_client=600, timeout_server=600, tracker_addr=None, index_page=None, resource_files=None, ): self.proc = PopenWorker() # send the function self.proc.send( _popen_start_proxy_server, [ host, port, port_end, web_port, timeout_client, timeout_server, tracker_addr, index_page, resource_files, ], ) # receive the port self.port = self.proc.recv() self.host = host def terminate(self): """Terminate the server process""" if self.proc: logging.info("Terminating Proxy Server...") self.proc.kill() self.proc = None def __del__(self): self.terminate()
def test_popen_worker_recycles(): proc = PopenWorker(maximum_uses=2) proc.send(os.getpid) initial_pid = proc.recv() assert psutil.pid_exists(initial_pid) proc.send(os.getpid) assert proc.recv() == initial_pid assert psutil.pid_exists(initial_pid) proc.send(os.getpid) assert proc.recv() != initial_pid assert not psutil.pid_exists(initial_pid)
def test_popen_worker_recycles_with_initializer(): initargs = [1, 2, 3] proc = PopenWorker(initializer=initializer, initargs=initargs, maximum_uses=3) proc.send(os.getpid) initial_pid = proc.recv() proc.send(after_initializer) assert list(proc.recv()) == initargs proc.send(os.getpid) assert proc.recv() == initial_pid # The process should be recycled with this send. proc.send(os.getpid) assert proc.recv() != initial_pid # But the initializer should've run this time as well. proc.send(after_initializer) assert list(proc.recv()) == initargs