From 7059ba1e6a7c36f44226bbaed41d9e2f8a994d6d Mon Sep 17 00:00:00 2001 From: Eddie Hebert Date: Wed, 11 Feb 2015 11:56:47 -0500 Subject: [PATCH] PERF: Avoid using attribute access in inner event processing. Reduce overhead of the attribute access of grabbing the process_event and process_trade from both the algorithm object and the perf_tracekr or blotter, by assigning those functions to a variable once per snapshot. --- zipline/gens/tradesimulation.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/zipline/gens/tradesimulation.py b/zipline/gens/tradesimulation.py index 2c4907a1..edd7ea11 100644 --- a/zipline/gens/tradesimulation.py +++ b/zipline/gens/tradesimulation.py @@ -83,12 +83,11 @@ class AlgorithmSimulator(object): return self.EMISSION_TO_PERF_KEY_MAP[ self.algo.perf_tracker.emission_rate] - def process_event(self, event): - process_trade = self.algo.blotter.process_trade - for txn, order in process_trade(event): - self.algo.perf_tracker.process_event(txn) - self.algo.perf_tracker.process_event(order) - self.algo.perf_tracker.process_event(event) + def _process_event(self, blotter_process_trade, perf_process_event, event): + for txn, order in blotter_process_trade(event): + perf_process_event(txn) + perf_process_event(order) + perf_process_event(event) def transform(self, stream_in): """ @@ -210,6 +209,15 @@ class AlgorithmSimulator(object): if instant_fill: events_to_be_processed = [] + # Assign process events to variables to avoid attribute access in + # innermost loops. + # + # Done here, to allow for perf_tracker or blotter to be swapped out + # or changed in between snapshots. + perf_process_event = self.algo.perf_tracker.process_event + blotter_process_trade = self.algo.blotter.process_trade + process_event = self._process_event + for event in snapshot: if event.type == DATASOURCE_TYPE.TRADE: @@ -223,24 +231,30 @@ class AlgorithmSimulator(object): self.update_universe(event) elif event.type == DATASOURCE_TYPE.SPLIT: + # process_split is not assigned to a variable since it is + # called rarely compared to the other event processors. self.algo.blotter.process_split(event) if not instant_fill: - self.process_event(event) + process_event(blotter_process_trade, + perf_process_event, + event) else: events_to_be_processed.append(event) if any_trade_occurred: new_orders = self._call_handle_data() for order in new_orders: - self.algo.perf_tracker.process_event(order) + perf_process_event(order) if instant_fill: # Now that handle_data has been called and orders have been placed, # process the event stream to fill user orders based on the events # from this snapshot. for event in events_to_be_processed: - self.process_event(event) + process_event(blotter_process_trade, + perf_process_event, + event) if benchmark_event_occurred: return self.get_message(dt)