add index for groupId, improve startup speed

- skip updating buried cards on startup; it's expensive and we'll do that on
  deck close in the future
- add an index for groupId. Initial profiling indicates that groupId-based
  selective study is considerably faster in certain scenarios

The 50k element deck I'm testing with now opens and builds the queue in 40ms
on a cold cache, of which 34ms is the initial deck startup and 6ms the queue
build. Adding back the undo log and backups will of course increase this, but
this is a big improvement for checking due times in the deck browser.
This commit is contained in:
Damien Elmes 2011-03-03 01:15:39 +09:00
parent bb79b0e17c
commit b3ee91a9d5
3 changed files with 25 additions and 37 deletions

View file

@ -15,8 +15,7 @@ NEW_CARDS_DUE = 1
# sort order for day's new cards
NEW_TODAY_ORDINAL = 0
NEW_TODAY_FACT = 1
NEW_TODAY_DUE = 2
NEW_TODAY_DUE = 1
# review card sort order
REV_CARDS_OLD_FIRST = 0

View file

@ -105,7 +105,6 @@ class Deck(object):
self.reps = 0
self.sched = Scheduler(self)
def modifiedSinceSave(self):
return self.modified > self.lastLoaded
@ -2637,11 +2636,9 @@ seq > :s and seq <= :e order by seq desc""", s=start, e=end)
required = []
if self.limits['newTodayOrder'] == NEW_TODAY_ORDINAL:
required.append("ordinal")
elif self.limits['newTodayOrder'] == NEW_TODAY_FACT:
required.append("factId")
if self.config['revCardOrder'] in (REV_CARDS_OLD_FIRST, REV_CARDS_NEW_FIRST):
required.append("interval")
cols = ["queue", "due"] + required
cols = ["queue", "due", "groupId"] + required
# update if changed
if self.db.scalar(
"select 1 from sqlite_master where name = 'ix_cards_multi'"):

View file

@ -21,12 +21,6 @@ class Scheduler(object):
self.queueLimit = 200
self.learnLimit = 1000
self.updateCutoff()
# restore any cards temporarily suspended by alternate schedulers
try:
self.resetSchedBuried()
except OperationalError, e:
# will fail if deck hasn't been upgraded yet
print "resetSched() failed"
def getCard(self, orm=True):
"Pop the next card from the queue. None if finished."
@ -39,9 +33,13 @@ class Scheduler(object):
def reset(self):
self.resetConfig()
t = time.time()
self.resetLearn()
print "lrn %0.2fms" % ((time.time() - t)*1000); t = time.time()
self.resetReview()
print "rev %0.2fms" % ((time.time() - t)*1000); t = time.time()
self.resetNew()
print "new %0.2fms" % ((time.time() - t)*1000); t = time.time()
def answerCard(self, card, ease):
if card.queue == 0:
@ -103,11 +101,10 @@ class Scheduler(object):
self.newQueue = []
self.newCount = 0
else:
self.newQueue = self.db.all(
self.cardLimit(
"newActive", "newInactive", """
select id, %s from cards c where
queue = 2 order by due limit %d""" % (self.newOrder(), lim)))
self.newQueue = self.db.all("""
select id %s from cards where
queue = 2 %s order by due limit %d""" % (self.newOrder(), self.groupLimit('new'),
lim))
self.newQueue.sort(key=itemgetter(1), reverse=True)
self.newCount = len(self.newQueue)
self.updateNewCardRatio()
@ -117,10 +114,7 @@ queue = 2 order by due limit %d""" % (self.newOrder(), lim)))
return self.newQueue.pop()[0]
def newOrder(self):
return ("ordinal",
"factId",
"due",
)[self.deck.limits['newTodayOrder']]
return (",ordinal", "")[self.deck.limits['newTodayOrder']]
def updateNewCardRatio(self):
if self.deck.config['newCardSpacing'] == NEW_CARDS_DISTRIBUTE:
@ -215,12 +209,13 @@ limit %d""" % self.learnLimit, lim=self.dayCutoff)
##########################################################################
def resetReview(self):
self.revCount = self.db.scalar(
self.cardLimit(
"revActive", "revInactive",
"select count(*) from cards c where queue = 1 "
"and due < :lim"), lim=self.dayCutoff)
self.revQueue = []
self.revQueue = self.db.all("""
select id from cards where
queue = 1 %s and due < :lim order by %s limit %d""" % (
self.groupLimit("rev"), self.revOrder(), self.queueLimit),
lim=self.dayCutoff)
self.revQueue.reverse()
self.revCount = len(self.revQueue)
def getReviewCard(self):
if self.haveRevCards():
@ -232,21 +227,13 @@ limit %d""" % self.learnLimit, lim=self.dayCutoff)
self.fillRevQueue()
return self.revQueue
def fillRevQueue(self):
self.revQueue = self.db.all(
self.cardLimit(
"revActive", "revInactive", """
select c.id, factId from cards c where
queue = 1 and due < :lim order by %s
limit %d""" % (self.revOrder(), self.queueLimit)), lim=self.dayCutoff)
self.revQueue.reverse()
# FIXME: current random order won't work with new spacing
# FIXME: limits for new, config for rev is strange atm
def revOrder(self):
return ("interval desc",
"interval",
"due",
"factId, ordinal")[self.revCardOrder]
"factId, ordinal")[self.deck.config['revCardOrder']]
# FIXME: rewrite
def showFailedLast(self):
@ -492,6 +479,11 @@ and queue between 1 and 2""",
self.db.statement(
"update cards set queue = type where queue = -3")
def groupLimit(self, type):
#return " and groupId in (1)"
print "fixme: groupLimit()"
return ""
def cardLimit(self, active, inactive, sql):
yes = parseTags(self.deck.limits.get(active))
no = parseTags(self.deck.limits.get(inactive))