Skip to content
This repository was archived by the owner on Jan 10, 2023. It is now read-only.

Commit 55bcd69

Browse files
committed
Adding more perf regression tests (joins, index cost).
Specifically: - Adding a new curve in existing SelectSingleRow graph, which demonstrates current performance pain point in BTree#cost(). - Adding a new graph for joins with curves for theta-join and equi-join accordingly. ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=82523214
1 parent f5ed5b1 commit 55bcd69

File tree

3 files changed

+152
-8
lines changed

3 files changed

+152
-8
lines changed

perf/lf_perf_test.js

+12
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ function selectRunner(name, db) {
257257
'SelectSingleRowNonIndexed',
258258
selectBenchmark.querySingleRowNonIndexed.bind(selectBenchmark),
259259
selectBenchmark.verifySingleRowNonIndexed.bind(selectBenchmark));
260+
benchmarkRunner.schedule(
261+
'SelectSingleRowMultipleIndices',
262+
selectBenchmark.querySingleRowMultipleIndices.bind(selectBenchmark),
263+
selectBenchmark.verifySingleRowMultipleIndices.bind(selectBenchmark));
260264
benchmarkRunner.schedule(
261265
'SelectMultiRowIndexedRange',
262266
selectBenchmark.queryMultiRowIndexedRange.bind(selectBenchmark),
@@ -295,6 +299,14 @@ function selectRunner(name, db) {
295299
'SelectProjectAggregateNonIndexed',
296300
selectBenchmark.queryProjectAggregateNonIndexed.bind(selectBenchmark),
297301
selectBenchmark.verifyProjectAggregateNonIndexed.bind(selectBenchmark));
302+
benchmarkRunner.schedule(
303+
'SelectJoinEqui',
304+
selectBenchmark.queryJoinEqui.bind(selectBenchmark),
305+
selectBenchmark.verifyJoinEqui.bind(selectBenchmark));
306+
benchmarkRunner.schedule(
307+
'SelectJoinTheta',
308+
selectBenchmark.queryJoinTheta.bind(selectBenchmark),
309+
selectBenchmark.verifyJoinTheta.bind(selectBenchmark));
298310

299311
return benchmarkRunner.run(REPETITIONS);
300312
}).then(tearDown, tearDown);

testing/hr_schema/mock_data_generator.js

+37-8
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ lf.testing.hrSchema.MockDataGenerator = function(schema) {
3737
/** @private {!hr.db.schema.Database} */
3838
this.schema_ = schema;
3939

40-
/** @type {!Array.<!hr.db.row.Job>} */
40+
/** @type {!Array<!hr.db.row.Job>} */
4141
this.sampleJobs = [];
4242

43-
/** @type {!Array.<!hr.db.row.Employee>} */
43+
/** @type {!Array<!hr.db.row.Employee>} */
4444
this.sampleEmployees = [];
4545

46-
/** @type {!Array.<!hr.db.row.Department>} */
46+
/** @type {!Array<!hr.db.row.Department>} */
4747
this.sampleDepartments = [];
4848

4949
/** @type {!lf.testing.hrSchema.MockDataGenerator.JobGroundTruth} */
@@ -63,7 +63,8 @@ lf.testing.hrSchema.MockDataGenerator = function(schema) {
6363
* countSalary: number,
6464
* minHireDate: !Date,
6565
* maxHireDate: !Date,
66-
* employeesPerJob: !goog.labs.structs.Multimap<string, string>
66+
* employeesPerJob: !goog.labs.structs.Multimap<string, string>,
67+
* thetaJoinSalaryIds: !Array<string>
6768
* }}
6869
*/
6970
lf.testing.hrSchema.MockDataGenerator.EmployeeGroundTruth;
@@ -73,13 +74,13 @@ lf.testing.hrSchema.MockDataGenerator.EmployeeGroundTruth;
7374
* @typedef {{
7475
* minMinSalary: number,
7576
* maxMinSalary: number,
76-
* distinctMinSalary: !Array.<number>,
77+
* distinctMinSalary: !Array<number>,
7778
* countDistinctMinSalary: number,
7879
* avgDistinctMinSalary: number,
7980
* stddevDistinctMinSalary: number,
8081
* minMaxSalary: number,
8182
* maxMaxSalary: number,
82-
* distinctMaxSalary: !Array.<number>,
83+
* distinctMaxSalary: !Array<number>,
8384
* countDistinctMaxSalary: number,
8485
* avgDistinctMaxSalary: number,
8586
* stddevDistinctMaxSalary: number,
@@ -172,7 +173,8 @@ lf.testing.hrSchema.MockDataGenerator.prototype.extractEmployeeGroundTruth_ =
172173
null, this.sampleEmployees.map(salary)),
173174
countSalary: 0,
174175
minHireDate: this.findEmployeeMinDate_(),
175-
maxHireDate: this.findEmployeeMaxDate_()
176+
maxHireDate: this.findEmployeeMaxDate_(),
177+
thetaJoinSalaryIds: this.findThetaJoinSalaryIds_()
176178
};
177179
};
178180

@@ -211,7 +213,7 @@ lf.testing.hrSchema.MockDataGenerator.prototype.findJobMax_ = function(
211213
* Finds the DISTINCT of a given attribute in the Job table.
212214
* @param {!function(!hr.db.row.Job): number} getterFn The function to call for
213215
* accessing the attribute of interest.
214-
* @return {!Array.<number>} The distinct values.
216+
* @return {!Array<number>} The distinct values.
215217
* @private
216218
*/
217219
lf.testing.hrSchema.MockDataGenerator.prototype.findJobDistinct_ = function(
@@ -310,6 +312,33 @@ lf.testing.hrSchema.MockDataGenerator.prototype.findEmployeeMaxDate_ =
310312
};
311313

312314

315+
/**
316+
* Finds the IDs of all employees whose salary is larger than the MAX salary for
317+
* their job title.
318+
* Note: This is possible because generated employee data does not respect
319+
* corresponding min/max job salary.
320+
* @return {!Array<string>} The employee IDs in ascending sorted order.
321+
* @private
322+
*/
323+
lf.testing.hrSchema.MockDataGenerator.prototype.findThetaJoinSalaryIds_ =
324+
function() {
325+
var employeeIds = [];
326+
327+
for (var i = 0; i < this.sampleEmployees.length; i++) {
328+
for (var j = 0; j < this.sampleJobs.length; j++) {
329+
var employee = this.sampleEmployees[i];
330+
var job = this.sampleJobs[j];
331+
if (employee.getJobId() == job.getId() &&
332+
employee.getSalary() > job.getMaxSalary()) {
333+
employeeIds.push(employee.getId());
334+
}
335+
}
336+
}
337+
338+
return employeeIds.sort();
339+
};
340+
341+
313342
/**
314343
* @typedef {{
315344
* departments: !Array<!hr.db.row.DepartmentDbType>,

testing/perf/select_benchmark.js

+103
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ goog.provide('lf.testing.perf.SelectBenchmark');
1919
goog.require('goog.math');
2020
goog.require('lf.Order');
2121
goog.require('lf.fn');
22+
goog.require('lf.op');
2223

2324

2425

@@ -209,6 +210,50 @@ lf.testing.perf.SelectBenchmark.prototype.querySingleRowNonIndexed =
209210
};
210211

211212

213+
/**
214+
* Note: The following query uses two ValuePredicates that both operate on an
215+
* indexed property. This causes the IndexRangeScanPass to select the most
216+
* selective index for the IndexRangeScan step by calling BTree#cost(), and
217+
* because of the TODO that exists in that method performance is very poor.
218+
* Should observe a noticeable change once BTree#cost() is optimized.
219+
*
220+
* @return {!IThenable}
221+
*/
222+
lf.testing.perf.SelectBenchmark.prototype.querySingleRowMultipleIndices =
223+
function() {
224+
// Selecting a value purposefully high such that all rows satisfy this
225+
// predicate, whereas the other predicate is satisfied by only one row.
226+
var salaryThreshold = 2 * this.dataGenerator_.employeeGroundTruth.avgSalary;
227+
228+
var q = this.db_.
229+
select().
230+
from(this.e_).
231+
orderBy(this.e_.id, lf.Order.ASC).
232+
where(lf.op.and(
233+
this.e_.id.eq(this.queryData_.employeeId),
234+
this.e_.salary.lt(salaryThreshold)));
235+
236+
return q.exec();
237+
};
238+
239+
240+
/**
241+
* @param {!Array.<!Object>} results
242+
* @return {!IThenable.<boolean>}
243+
*/
244+
lf.testing.perf.SelectBenchmark.prototype.verifySingleRowMultipleIndices =
245+
function(results) {
246+
var salaryThreshold = 2 * this.dataGenerator_.employeeGroundTruth.avgSalary;
247+
248+
var validated = results.every(function(obj) {
249+
return obj[this.e_.id.getName()] == this.queryData_.employeeId &&
250+
obj[this.e_.salary.getName()] < salaryThreshold;
251+
}, this);
252+
253+
return goog.Promise.resolve(validated);
254+
};
255+
256+
212257
/**
213258
* @param {!Array.<!Object>} results
214259
* @return {!IThenable.<boolean>}
@@ -528,3 +573,61 @@ lf.testing.perf.SelectBenchmark.prototype.verifyProjectAggregateNonIndexed =
528573

529574
return goog.Promise.resolve(validated);
530575
};
576+
577+
578+
/** @return {!IThenable} */
579+
lf.testing.perf.SelectBenchmark.prototype.queryJoinEqui = function() {
580+
return this.db_.
581+
select().
582+
from(this.e_, this.j_).
583+
where(this.e_.jobId.eq(this.j_.id)).
584+
exec();
585+
};
586+
587+
588+
/**
589+
* @param {!Array.<!Object>} results
590+
* @return {!IThenable.<boolean>}
591+
*/
592+
lf.testing.perf.SelectBenchmark.prototype.verifyJoinEqui = function(results) {
593+
if (this.dataGenerator_.sampleEmployees.length != results.length) {
594+
return goog.Promise.resolve(false);
595+
}
596+
597+
var validated = results.every(function(obj) {
598+
return goog.object.getCount(obj) == 2 &&
599+
goog.isDefAndNotNull(obj[this.e_.getName()]) &&
600+
goog.isDefAndNotNull(obj[this.j_.getName()]) &&
601+
obj[this.e_.getName()][this.e_.jobId.getName()] ==
602+
obj[this.j_.getName()][this.j_.id.getName()];
603+
}, this);
604+
605+
return goog.Promise.resolve(validated);
606+
};
607+
608+
609+
/** @return {!IThenable} */
610+
lf.testing.perf.SelectBenchmark.prototype.queryJoinTheta = function() {
611+
return this.db_.
612+
select().
613+
from(this.j_, this.e_).
614+
orderBy(this.e_.id, lf.Order.ASC).
615+
where(lf.op.and(
616+
this.e_.jobId.eq(this.j_.id),
617+
this.e_.salary.gt(this.j_.maxSalary))).
618+
exec();
619+
};
620+
621+
622+
/**
623+
* @param {!Array.<!Object>} results
624+
* @return {!IThenable.<boolean>}
625+
*/
626+
lf.testing.perf.SelectBenchmark.prototype.verifyJoinTheta = function(results) {
627+
var validated = results.every(function(obj, i) {
628+
return obj[this.e_.getName()][this.e_.id.getName()] ==
629+
this.dataGenerator_.employeeGroundTruth.thetaJoinSalaryIds[i];
630+
}, this);
631+
632+
return goog.Promise.resolve(validated);
633+
};

0 commit comments

Comments
 (0)