Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory metrics for Chrome using window.performance.memory #48

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ Thumbnails
*.sublime-workspace
*.sublime-project

.tern-project
62 changes: 62 additions & 0 deletions docs/MemoryMetrics.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"totalJSHeapSize_max": {
"type": "max",
"tags": ["Script"],
"unit": "bytes",
"source": "MemoryMetrics",
"importance": 50,
"summary": "Maximum size (peak) of JavaScript heap including unused space",
"details": "",
"browsers": ["chrome"]
},
"totalJSHeapSize_min": {
"type": "min",
"tags": ["Script"],
"unit": "bytes",
"source": "MemoryMetrics",
"importance": 20,
"summary": "Minimum size of JavaScript heap including unused space",
"details": "",
"browsers": ["chrome"]
},
"totalJSHeapSize_avg": {
"type": "average",
"tags": ["Script"],
"unit": "bytes",
"source": "MemoryMetrics",
"importance": 70,
"summary": "Average size of JavaScript heap including unused space",
"details": "",
"browsers": ["chrome"]
},
"usedJSHeapSize_max": {
"type": "max",
"tags": ["Script"],
"unit": "bytes",
"source": "MemoryMetrics",
"importance": 50,
"summary": "Maximum size (peak) of JavaScript heap excluding unused space",
"details": "",
"browsers": ["chrome"]
},
"usedJSHeapSize_min": {
"type": "min",
"tags": ["Script"],
"unit": "bytes",
"source": "MemoryMetrics",
"importance": 20,
"summary": "Minimum size of JavaScript heap excluding unused space",
"details": "",
"browsers": ["chrome"]
},
"usedJSHeapSize_avg": {
"type": "average",
"tags": ["Script"],
"unit": "bytes",
"source": "MemoryMetrics",
"importance": 70,
"summary": "Average size of JavaScript heap excluding unused space",
"details": "",
"browsers": ["chrome"]
}
}
65 changes: 65 additions & 0 deletions lib/metrics/MemoryMetrics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
var Q = require('q'),
debug = require('debug')('bp:metrics:MemoryMetrics'),
BaseMetrics = require('./BaseMetrics');

function MemoryMetrics(probes) {
BaseMetrics.apply(this, [{
probes: probes
}]);
}
require('util').inherits(MemoryMetrics, BaseMetrics);

MemoryMetrics.prototype.id = 'MemoryMetrics';
MemoryMetrics.prototype.probes = ['MemoryProbe'];

MemoryMetrics.prototype.setup = function() {
this.results = [];
}

MemoryMetrics.prototype.start = function() {
}

MemoryMetrics.prototype.onData = function(data) {
this.results.push(data);
}

MemoryMetrics.prototype.onError = function() {
debug('onError Method called');
}

MemoryMetrics.prototype.getResults = function() {
var totalmin = Number.MAX_VALUE, totalmax = 0, totalavg = 0,
usedmin = Number.MAX_VALUE, usedmax = 0, usedavg = 0;

this.results.forEach(function(v) {
if(v.totalJSHeapSize < totalmin) {
totalmin = v.totalJSHeapSize;
}
if(v.totalJSHeapSize > totalmax) {
totalmax = v.totalJSHeapSize;
}
totalavg += v.totalJSHeapSize;

if(v.usedJSHeapSize < usedmin) {
usedmin = v.usedJSHeapSize;
}
if(v.usedJSHeapSize > usedmax) {
usedmax = v.usedJSHeapSize;
}
usedavg += v.usedJSHeapSize;
});

usedavg /= this.results.length;
totalavg /= this.results.length;

return {
totalJSHeapSize_max: totalmax,
totalJSHeapSize_min: totalmin,
totalJSHeapSize_avg: totalavg,
usedJSHeapSize_max: usedmax,
usedJSHeapSize_min: usedmin,
usedJSHeapSize_avg: usedavg,
}
}

module.exports = MemoryMetrics;
5 changes: 3 additions & 2 deletions lib/metrics/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,6 @@ module.exports.builtIns = [
'ChromeTracingMetrics',
'RafRenderingStats',
'NetworkTimings',
'NetworkResources'
];
'NetworkResources',
'MemoryMetrics'
];
37 changes: 37 additions & 0 deletions lib/probes/MemoryProbe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
var Q = require('q'),
util = require('util'),
events = require('events'),
helpers = require('../helpers'),
debug = require('debug')('bp:probes:MemoryProbe');

function MemoryProbe(id) {
if (id) {
this.id = id;
}
events.EventEmitter.call(this);
}

util.inherits(MemoryProbe, events.EventEmitter);

MemoryProbe.prototype.id = 'MemoryProbe';

MemoryProbe.prototype.setup = function(cfg) {
this.timerHandle = null;
};

MemoryProbe.prototype.start = function(browser) {
var me = this;
this.timerHandle = setInterval(function() {
browser.eval('window.performance.memory').then(function(res) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling eval with a time from webdriver usually results in an extra network call, since the webdriver would have to talk to selenium. This will require the extension to receive the "stringified" message, parse and execute it, increasing the impact of the test on the final data that we get.

Also note that when you collect memory with metrics like frames_per_sec, you may notice differences since this is additional javascript that is executed, and results in network calls. Could that be the reason you are getting some inconsistent results in your metrics ?

if(res) {
me.emit('data', res);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to suggest that instead of getting this and emiting it back to browser-perf, we could store all these results in the browser, and then finally send it all back, but I realize that accumulating the result in the browser could lead to increased memory, which adds extra noise due to this test runner.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am noticing some inconsistencies when the window gets scrolled (browser scripts seem to freeze). I suspect it is related to this code. Do you have any pointers? As you said, storing data browser-side would impact the results. An alternative would be to store max, min and last values browser-side. It would have minimum impact. Or remove the timer entirely and just return the last values before the test stops. Any other options?

}
});
}, 1000);
};

MemoryProbe.prototype.teardown = function(cfg, browser) {
clearInterval(this.timerHandle);
};

module.exports = MemoryProbe;