PERF: Optimized rewrite of HistoryContainer.update.

Uses a numpy array instead of a dict of dicts when initializing history
container.

In testing this reduced the total time spent in HistoryContainer.update
by 66%.

BEFORE COMMIT:
Thu Oct 16 22:30:46 2014    results/cprofile/unoptimized

         185223320 function calls (182210491 primitive calls) in 401.351
         seconds

   Ordered by: cumulative time
   List reduced from 2398 to 27 due to restriction <'update'>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     8580    0.461    0.000  160.571    0.019
     qexec/zipline/history/history_container.py:388(update)

AFTER COMMIT:
Thu Oct 16 22:12:28 2014    results/cprofile/optimized

         143177181 function calls (140164352 primitive calls) in 272.403
         seconds

   Ordered by: cumulative time
   List reduced from 2395 to 27 due to restriction <'update'>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     8580    0.086    0.000   47.294    0.006 qexec/zipline/history/history_container.py:388(update)
This commit is contained in:
Scott Sanderson
2014-10-16 22:32:43 -04:00
parent 50c5b73a7b
commit 30c4a1a1dc
2 changed files with 27 additions and 11 deletions
+24 -11
View File
@@ -361,22 +361,35 @@ class HistoryContainer(object):
# earliest_minute and latest_minute, which is what we want.
return buffer_panel.ix[:, earliest_minute:latest_minute, :]
def frame_from_bardata(self, data, algo_dt):
"""
Create a DataFrame from the given BarData and algo dt.
"""
data = data._data
frame_data = np.ones((len(self.sids), len(self.fields))) * np.nan
for i, sid in enumerate(self.sids):
sid_data = data.get(sid)
if not sid_data:
continue
if algo_dt != sid_data['dt']:
continue
for j, field in enumerate(self.fields):
frame_data[i, j] = sid_data.get(field, np.nan)
return pd.DataFrame(
frame_data,
index=self.sids.copy(),
columns=self.fields.copy(),
).T
def update(self, data, algo_dt):
"""
Takes the bar at @algo_dt's @data, checks to see if we need to roll any
new digests, then adds new data to the buffer panel.
"""
frame = pd.DataFrame(
{
sid: {field: bar[field] for field in self.fields}
for sid, bar in data.iteritems()
# data contains the latest values seen for each security in our
# universe. If a stock didn't trade this dt, then it will
# still have an entry in data, but its dt will be behind the
# algo_dt.
if (bar and bar['dt'] == algo_dt and sid in self.sids)
}
)
frame = self.frame_from_bardata(data, algo_dt)
self.update_last_known_values()
self.update_digest_panels(algo_dt, self.buffer_panel)
+3
View File
@@ -210,6 +210,9 @@ class SIDData(object):
"""
return self.dt
def get(self, name, default):
return self.__dict__.get(name, default)
def __getitem__(self, name):
return self.__dict__[name]