From 08f58415222e12adbdb2f785597bf35c57439388 Mon Sep 17 00:00:00 2001 From: Asmageddon Date: Mon, 4 Jun 2012 17:44:06 +0200 Subject: [PATCH] Performance improvements - console should now be much faster with many torrents and slightly faster to somewhat slower with few --- deluge/ui/console/modes/alltorrents.py | 45 +++++++++++++++++-------- deluge/ui/console/modes/column.py | 13 +++---- deluge/ui/console/modes/format_utils.py | 15 +++++---- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/deluge/ui/console/modes/alltorrents.py b/deluge/ui/console/modes/alltorrents.py index a4400179d..2e9c09138 100644 --- a/deluge/ui/console/modes/alltorrents.py +++ b/deluge/ui/console/modes/alltorrents.py @@ -256,8 +256,9 @@ SEARCH_END_REACHED = 4 class AllTorrents(BaseMode, component.Component): def __init__(self, stdscr, encoding=None): - self.formatted_rows = None self.torrent_names = None + self.numtorrents = -1 + self._cached_rows = {} self.cursel = 1 self.curoff = 1 # TODO: this should really be 0 indexed self.column_string = "" @@ -411,15 +412,13 @@ class AllTorrents(BaseMode, component.Component): def set_state(self, state, refresh): self.curstate = state # cache in case we change sort order newnames = [] - newrows = [] + self._cached_rows = {} self._sorted_ids = self._sort_torrents(self.curstate) for torrent_id in self._sorted_ids: ts = self.curstate[torrent_id] newnames.append(ts["name"]) - newrows.append((format_utils.format_row([column.get_column_value(name,ts) for name in self.__columns],self.column_widths),ts["state"])) self.numtorrents = len(state) - self.formatted_rows = newrows self.torrent_names = newnames if refresh: self.refresh() @@ -529,6 +528,7 @@ class AllTorrents(BaseMode, component.Component): cmp_func = self._queue_sort + sg = state.get def sort_by_field(state, result, field): if field in column_names_to_state_keys: field = column_names_to_state_keys[field] @@ -541,8 +541,8 @@ class AllTorrents(BaseMode, component.Component): if field in first_element: is_string = isinstance( first_element[field], basestring) - sort_key = lambda s:state.get(s)[field] - sort_key2 = lambda s:state.get(s)[field].lower() + sort_key = lambda s:sg(s)[field] + sort_key2 = lambda s:sg(s)[field].lower() #If it's a string, sort case-insensitively but preserve A>a order if is_string: @@ -806,23 +806,40 @@ class AllTorrents(BaseMode, component.Component): pass # add all the torrents - if self.formatted_rows == []: + if self.numtorrents == 0: msg = "No torrents match filter".center(self.cols) self.add_string(3, "{!info!}%s"%msg) - elif self.formatted_rows: + elif self.numtorrents > 0: tidx = self.curoff currow = 2 + #Because dots are slow + sorted_ids = self._sorted_ids + curstate = self.curstate + gcv = column.get_column_value + fr = format_utils.format_row + cols = self.__columns + colw = self.column_widths + cr = self._cached_rows + def draw_row(index): + if index not in cr: + ts = curstate[sorted_ids[index]] + cr[index] = (fr([gcv(name,ts) for name in cols],colw),ts["state"]) + return cr[index] + if lines: todraw = [] for l in lines: - try: - todraw.append(self.formatted_rows[l]) - except: - pass #A quick and ugly fix for crash caused by doing shift-m on last torrent + if l < tidx - 1: continue + if l >= tidx - 1 + self.rows - 3: break + if l >= self.numtorrents: break + todraw.append(draw_row(l)) lines.reverse() else: - todraw = self.formatted_rows[tidx-1:] + todraw = [] + for i in range(tidx-1, tidx-1 + self.rows - 3): + if i >= self.numtorrents: break + todraw += [draw_row(i)] for row in todraw: # default style @@ -1083,7 +1100,7 @@ class AllTorrents(BaseMode, component.Component): reactor.stop() return - if self.formatted_rows==None or self.popup: + if self.numtorrents == 0 or self.popup: return elif self.entering_search: diff --git a/deluge/ui/console/modes/column.py b/deluge/ui/console/modes/column.py index fcc7878a0..dd89df02b 100644 --- a/deluge/ui/console/modes/column.py +++ b/deluge/ui/console/modes/column.py @@ -75,18 +75,15 @@ def get_column_value(name,state): log.error("No such column: %s",name) return None - if col[1] != None: - args = [] + if col[1]: try: - for key in col[0]: - args.append(state[key]) + args = [ state[key] for key in col[0] ] except: log.error("Could not get column field: %s",col[0]) return None - colval = col[1](*args) + return col[1](*args) else: - colval = state[col[0][0]] - return colval + return state[col[0][0]] def get_required_fields(cols): @@ -96,4 +93,4 @@ def get_required_fields(cols): return fields - + diff --git a/deluge/ui/console/modes/format_utils.py b/deluge/ui/console/modes/format_utils.py index 4c0ece117..58f3c495e 100644 --- a/deluge/ui/console/modes/format_utils.py +++ b/deluge/ui/console/modes/format_utils.py @@ -104,15 +104,18 @@ def trim_string(string, w, have_dbls): else: return u"%s "%(string[0:w-1]) +#Dots are slow +eaw = unicodedata.east_asian_width +ud_normalize = unicodedata.normalize def format_column(col, lim): dbls = 0 - if haveud and isinstance(col,unicode): + #Chosen over isinstance(col, unicode) and col.__class__ == unicode + # for speed - it's ~3 times faster for non-unicode strings and ~1.5 + # for unicode strings. + if haveud and col.__class__ is unicode: # might have some double width chars - col = unicodedata.normalize("NFC",col) - for c in col: - if unicodedata.east_asian_width(c) in ['W','F']: - # found a wide/full char - dbls += 1 + col = ud_normalize("NFC",col) + dbls = sum(eaw(c) in 'WF' for c in col) size = len(col)+dbls if (size >= lim - 1): return trim_string(col,lim,dbls>0)