diff --git a/codespeed/models.py b/codespeed/models.py
index a317a48c..1bc24803 100644
--- a/codespeed/models.py
+++ b/codespeed/models.py
@@ -401,34 +401,41 @@ def get_changes_table(self, trend_depth=10, force_save=False):
return self._get_tablecache()
# Otherwise generate a new changes table
# Get latest revisions for this branch (which also sets the project)
- lastrevisions = self.get_last_revisions(trend_depth)
+ lastrevisions = list(self.get_last_revisions(trend_depth))
if not lastrevisions:
return []
- change_list = []
+ changerevision = None
pastrevisions = []
if len(lastrevisions) > 1:
changerevision = lastrevisions[1]
- change_list = Result.objects.filter(
- revision=changerevision
- ).filter(
- environment=self.environment
- ).filter(
- executable=self.executable
- )
pastrevisions = lastrevisions[trend_depth - 2:trend_depth + 1]
- result_list = Result.objects.filter(
- revision=lastrevisions[0]
- ).filter(
- environment=self.environment
- ).filter(
- executable=self.executable
- )
+ # Bulk fetch all results needed across current, change, and past revisions
+ relevant_revs = [lastrevisions[0]]
+ if changerevision:
+ relevant_revs.append(changerevision)
+ relevant_revs.extend(pastrevisions)
+ results_map = {
+ (r.revision_id, r.benchmark_id): r
+ for r in Result.objects.filter(
+ revision__in=relevant_revs,
+ environment=self.environment,
+ executable=self.executable,
+ )
+ }
+
+ # Fetch and group all benchmarks in one query, preserving DB order
+ benchmarks_by_units = {}
+ for bench in Benchmark.objects.all():
+ benchmarks_by_units.setdefault(bench.units_title, []).append(bench)
+
+ current_rev_id = lastrevisions[0].pk
+ change_rev_id = changerevision.pk if changerevision else None
+ past_rev_ids = [rev.pk for rev in pastrevisions]
tablelist = []
- for units_title in Benchmark.objects.all().values_list(
- 'units_title', flat=True).distinct():
+ for units_title, bench_group in benchmarks_by_units.items():
currentlist = []
units = ""
hasmin = False
@@ -436,14 +443,13 @@ def get_changes_table(self, trend_depth=10, force_save=False):
has_stddev = False
smallest = 1000
totals = {'change': [], 'trend': []}
- for bench in Benchmark.objects.filter(units_title=units_title):
+ for bench in bench_group:
units = bench.units
lessisbetter = bench.lessisbetter
- resultquery = result_list.filter(benchmark=bench)
- if not len(resultquery):
- continue
- resobj = resultquery.filter(benchmark=bench)[0]
+ resobj = results_map.get((current_rev_id, bench.pk))
+ if resobj is None:
+ continue
std_dev = resobj.std_dev
if std_dev is not None:
@@ -466,13 +472,13 @@ def get_changes_table(self, trend_depth=10, force_save=False):
# Calculate percentage change relative to previous result
result = max(resobj.value, 0)
change = "-"
- if len(change_list):
- c = change_list.filter(benchmark=bench)
- if c.count() and result is not None:
- if c[0].value != 0:
- change = (result - c[0].value) * 100 / c[0].value
- totals['change'].append(result / c[0].value)
- elif c[0].value == 0:
+ if change_rev_id is not None:
+ c = results_map.get((change_rev_id, bench.pk))
+ if c is not None:
+ if c.value != 0:
+ change = (result - c.value) * 100 / c.value
+ totals['change'].append(result / c.value)
+ elif c.value == 0:
if result == 0:
# 0/0 = 1, in our world
change = 0
@@ -481,27 +487,16 @@ def get_changes_table(self, trend_depth=10, force_save=False):
# n/0 = ∞
change = float("inf")
totals['change'].append(float("inf"))
- else:
- # no previous result, no change available
- pass
# Calculate trend:
# percentage change relative to average of 3 previous results
- # Calculate past average
result_sum = 0
num_past_results = 0
- if len(pastrevisions):
- for rev in pastrevisions:
- past_result = Result.objects.filter(
- revision=rev
- ).filter(
- environment=self.environment
- ).filter(
- executable=self.executable
- ).filter(benchmark=bench)
- if past_result.count():
- result_sum += past_result[0].value
- num_past_results += 1
+ for rev_id in past_rev_ids:
+ past_r = results_map.get((rev_id, bench.pk))
+ if past_r is not None:
+ result_sum += past_r.value
+ num_past_results += 1
trend = "-"
if result_sum:
average = result_sum / num_past_results
diff --git a/codespeed/static/js/changes.js b/codespeed/static/js/changes.js
index 63a2d870..21e5895a 100644
--- a/codespeed/static/js/changes.js
+++ b/codespeed/static/js/changes.js
@@ -3,7 +3,7 @@ var Changes = (function(window){
// Localize globals
var TIMELINE_URL = window.TIMELINE_URL, getLoadText = window.getLoadText;
-var currentproject, changethres, trendthres, projectmatrix, revisionboxes = {};
+var currentproject, changethres, trendthres, projectmatrix, revisionboxes = {}, revisiondata = {}, envhasresults = {};
function getConfiguration(revision) {
return {
@@ -82,6 +82,19 @@ function updateTable() {
});
}
+function updateRevisionMarkers(env_id) {
+ var has = {};
+ $.each(envhasresults[env_id] || [], function(_, commitid) { has[commitid] = true; });
+ var current = $("#revision").val();
+ var options = "";
+ $.each(revisiondata[currentproject] || [], function(_, r) {
+ var marker = has[r[1]] ? '● ' : '○ ';
+ options += "";
+ });
+ $("#revision").html(options);
+ $("#revision").val(current);
+}
+
function refreshContent() {
refreshContentTable($("#revision option:selected").val());
}
@@ -110,8 +123,8 @@ function changeRevisions() {
selected_project = projectmatrix[executable];
if (selected_project !== currentproject) {
- $("#revision").html(revisionboxes[selected_project]);
currentproject = selected_project;
+ updateRevisionMarkers($("input[name='environment']:checked").val());
//Give visual cue that the select box has changed
var bgc = $("#revision").parent().parent().css("backgroundColor");
@@ -132,8 +145,10 @@ function config(c) {
function init(defaults) {
currentproject = defaults.project;
projectmatrix = defaults.projectmatrix;
+ envhasresults = defaults.envhasresults || {};
$.each(defaults.revisionlists, function(project, revs) {
+ revisiondata[project] = revs;
var options = "";
$.each(revs, function(index, r) {
options += "";
@@ -148,10 +163,14 @@ function init(defaults) {
$("input[name='executable']").change(changeRevisions);
$("#env" + defaults.environment).prop('checked', true);
- $("input[name='environment']").change(refreshContent);
+ $("input[name='environment']").change(function() {
+ updateRevisionMarkers($("input[name='environment']:checked").val());
+ refreshContent();
+ });
$("#revision").html(revisionboxes[defaults.project]);
$("#revision").val(defaults.revision);
+ updateRevisionMarkers(defaults.environment);
$("#revision").change(refreshContent);
$("#permalink").click(function() {
diff --git a/codespeed/templates/codespeed/changes.html b/codespeed/templates/codespeed/changes.html
index 4403b913..82fa8386 100644
--- a/codespeed/templates/codespeed/changes.html
+++ b/codespeed/templates/codespeed/changes.html
@@ -81,11 +81,12 @@
Changes.init({
project: "{{ defaultexecutable.project }}",
executable: "{{ defaultexecutable.id }}",
- environment: "{{ defaultenvironment.id }}",
+ environment: "{{ defaultenvironment.0.id }}",
revision: "{{ selectedrevision.commitid }}",
trend: "{{ defaulttrend }}",
projectmatrix: eval({{ projectmatrix|safe }}),
- revisionlists: eval({{ revisionlists|safe }})
+ revisionlists: eval({{ revisionlists|safe }}),
+ envhasresults: eval({{ env_has_results|safe }})
});
});
diff --git a/codespeed/views.py b/codespeed/views.py
index 0a13336e..97e9a920 100644
--- a/codespeed/views.py
+++ b/codespeed/views.py
@@ -908,6 +908,16 @@ def changes(request):
projectmatrix[e.id] = e.project.name
projectmatrix = json.dumps(projectmatrix)
+ all_commitids = [rev.commitid for revisions in revisionlists.values() for rev in revisions]
+ env_has_results = {}
+ for env in enviros:
+ has = set(Result.objects.filter(
+ environment=env,
+ revision__commitid__in=all_commitids,
+ ).values_list('revision__commitid', flat=True).distinct())
+ env_has_results[str(env.id)] = list(has)
+ env_has_results = json.dumps(env_has_results)
+
for project, revisions in revisionlists.items():
revisionlists[project] = [
(str(rev), rev.commitid) for rev in revisions
@@ -928,6 +938,7 @@ def changes(request):
'executables': executables,
'projectmatrix': projectmatrix,
'revisionlists': revisionlists,
+ 'env_has_results': env_has_results,
'trends': trends,
})