RideCache load performance improvement

.. string manipulation using raw C since its simple character
   replacement, halved time over previous approach.

.. lookup rideitem in ridecache via binary search (lower_bound)
   rather than serial. Minor speed up.

.. Overall, loading should be noticeably quicker for most users.
This commit is contained in:
Mark Liversedge
2020-07-30 15:44:02 +01:00
parent 11092bf436
commit 7a4be49492
7 changed files with 85 additions and 38 deletions

View File

@@ -58,7 +58,6 @@
Athlete::Athlete(Context *context, const QDir &homeDir)
{
fprintf(stderr, "new athlete preamble...\n"); fflush(stderr);
// athlete name / structured directory
this->home = new AthleteDirectoryStructure(homeDir);
this->context = context;

View File

@@ -124,12 +124,12 @@ RideCache::RideCache(Context *context) : context(context)
}
}
// now sort it - we need to use find on it
qSort(rides_.begin(), rides_.end(), rideCacheLessThan);
// load the store - will unstale once cache restored
load();
// now sort it
qSort(rides_.begin(), rides_.end(), rideCacheLessThan);
// set model once we have the basics
model_ = new RideCacheModel(context, this);
@@ -152,6 +152,20 @@ RideCache::RideCache(Context *context) : context(context)
connect(&watcher, SIGNAL(progressValueChanged(int)), this, SLOT(progressing(int)));
}
struct comparerideitem { bool operator()(const RideItem *p1, const RideItem *p2) { return p1->dateTime < p2->dateTime; } };
int
RideCache::find(RideItem *dt)
{
// use lower_bound to binary search
QVector<RideItem*>::const_iterator i = std::lower_bound(rides_.begin(), rides_.end(), dt, comparerideitem());
int index = i - rides_.begin();
// did it find the right value?
if (index < 0 || index >= rides_.count() || rides_.at(index)->dateTime != dt->dateTime) return -1;
return index;
}
RideCache::~RideCache()
{
exiting = true;

View File

@@ -102,6 +102,9 @@ class RideCache : public QObject
void load();
void save(bool opendata=false, QString filename="");
// find entry quickly
int find(RideItem *);
// user updated options/preferences
void configChanged(qint32);

View File

@@ -39,19 +39,44 @@
// Un-Escape special characters (JSON compliance)
static QString unprotect(char *string)
{
// sending UTF-8 to FLEX demands symetric conversion back to QString
QString string2 = QString::fromUtf8(string);
// this is a lexer string so it will be enclosed
// in quotes. Lets strip those first
QStringRef r = string2.midRef(1,string2.length()-2);
char *s = string;
char *target = static_cast<char*>(malloc(strlen(string)+1));
char *p = target;
// does it end with a space (to avoid token conflict) ?
if (r.endsWith(" ")) r = r.mid(0, r.length()-1);
// first "
s++;
QString s = Utils::RidefileUnEscape(r);
while (*s != '\0') {
if (*s == '\\') {
switch(*++s) {
case '\\' : *p++ = '\\'; break;
case 't' : *p++ = '\t'; break;
case 'n' : *p++ = '\n'; break;
case 'r' : *p++ = '\r'; break;
case 'b' : *p++ = '\b'; break;
case 'f' : *p++ = '\f'; break;
case '/' : *p++ = '/'; break;
case '"' : *p++ = '"'; break;
default:
*p++ = '\\'; *p++ = *s;
}
} else *p++ = *s;
s++;
}
return s;
// trailing "
p--;
// trailing SINGLE space used to protect tokens
*p-- = '\0';
if(p>=target && *p == ' ') *p = '\0';
// as a qstring
QString r = QString::fromUtf8(target);
free(target);
return r;
}
// we reimplement these to remove compiler warnings

View File

@@ -83,33 +83,21 @@ ride: '{' rideelement_list '}' {
#endif
} else {
// we're loading the cache
bool found = false;
foreach(RideItem *i, jc->cache->rides()) {
if (i->fileName == jc->item.fileName) {
double progress= double(jc->loading++) / double(jc->cache->rides().count()) * 100.0f;
if (jc->context->mainWindow->progress) {
found = true;
// progress update
double progress= double(jc->loading++) / double(jc->cache->rides().count()) * 100.0f;
if (jc->context->mainWindow->progress) {
// percentage progress
QString m = QString("%1%").arg(progress , 0, 'f', 0);
jc->context->mainWindow->progress->setText(m);
QApplication::processEvents();
} else {
jc->context->notifyLoadProgress(jc->folder,progress);
}
// update from our loaded value
i->setFrom(jc->item);
break;
}
// percentage progress
QString m = QString("%1%").arg(progress , 0, 'f', 0);
jc->context->mainWindow->progress->setText(m);
QApplication::processEvents();
} else {
jc->context->notifyLoadProgress(jc->folder,progress);
}
// not found !
if (found == false)
qDebug()<<"unable to load:"<<jc->item.fileName<<jc->item.dateTime<<jc->item.weight;
// find entry and update it
int index=jc->cache->find(&jc->item);
if (index==-1) qDebug()<<"unable to load:"<<jc->item.fileName<<jc->item.dateTime<<jc->item.weight;
else jc->cache->rides().at(index)->setFrom(jc->item);
}
// now set our ride item clean again, so we don't

View File

@@ -95,6 +95,8 @@ class RideItem : public QObject
public:
bool operator==(RideItem &other) { return other.dateTime == dateTime; }
Context *context; // to notify widgets when date/time changes
bool isdirty; // ride data has changed and needs saving
bool isstale; // metric data is out of date and needs recomputing

View File

@@ -84,6 +84,22 @@ AthleteCard::AthleteCard(ChartSpace *parent, QString path) : ChartSpaceItem(pare
painter.drawEllipse(0, 0, img.width(), img.height());
avatar = canvas.toImage();
#if 0
// ridecache raw
if (loadprogress == 0) {
QTime timer;
timer.start();
QFile rideDB(gcroot + "/" + path + "/cache/rideDB.json");
if (rideDB.exists() && rideDB.open(QFile::ReadOnly)) {
QByteArray contents = rideDB.readAll();
rideDB.close();
QJsonDocument json = QJsonDocument::fromJson(contents);
}
fprintf(stderr, "'%s' read rideDB took %d usecs\n", path.toStdString().c_str(), timer.elapsed()); fflush(stderr);
}
#endif
}
void