@tensorflow/tfjs-core
Version:
Hardware-accelerated JavaScript library for machine intelligence
199 lines • 8.02 kB
JavaScript
/**
* @license
* Copyright 2018 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
import * as tf from './index';
import { ALL_ENVS, describeWithFlags, SYNC_BACKEND_ENVS } from './jasmine_util';
import { checkComputationForErrors, Logger, Profiler } from './profiler';
class TestBackendTimer {
constructor(delayMs, queryTimeMs, extraInfo) {
this.delayMs = delayMs;
this.queryTimeMs = queryTimeMs;
this.extraInfo = extraInfo;
this.counter = 1;
}
timerAvailable() {
return true;
}
async time(query) {
query();
const kernelMs = await new Promise(resolve => setTimeout(() => resolve(this.queryTimeMs * this.counter++), this.delayMs));
return { kernelMs, getExtraProfileInfo: () => this.extraInfo };
}
}
class TestLogger extends Logger {
logKernelProfile(name, result, vals, timeMs) { }
}
function promiseCheckWrapper(acturalValPromise, truthVal) {
return acturalValPromise.then(acturalVal => {
expect(acturalVal).toEqual(truthVal);
});
}
function checkKernelProfile(acturalVal, truthVal) {
expect(acturalVal.kernelName).toBe(truthVal.kernelName);
expect(acturalVal.inputs).toBe(truthVal.inputs);
acturalVal.outputs.forEach((output, index) => {
expect(output).toBe(truthVal.outputs[index]);
});
const promiseContainer = [
promiseCheckWrapper(acturalVal.timeMs, truthVal.timeMs),
promiseCheckWrapper(acturalVal.extraInfo, truthVal.extraInfo),
];
return Promise.all(promiseContainer);
}
describeWithFlags('profiler.Profiler', SYNC_BACKEND_ENVS, () => {
it('profiles simple function', doneFn => {
const delayMs = 5;
const queryTimeMs = 10;
const inputs = { 'x': tf.tensor1d([1]) };
const extraInfo = '';
const timer = new TestBackendTimer(delayMs, queryTimeMs, extraInfo);
const logger = new TestLogger();
const profiler = new Profiler(timer, logger);
spyOn(timer, 'time').and.callThrough();
spyOn(logger, 'logKernelProfile').and.callThrough();
const timeSpy = timer.time;
let kernelCalled = false;
const result = 1;
const resultScalar = tf.scalar(result);
const kernelProfile = profiler.profileKernel('MatMul', inputs, () => {
kernelCalled = true;
return [resultScalar];
});
setTimeout(() => {
expect(timeSpy.calls.count()).toBe(1);
expect(kernelCalled).toBe(true);
checkKernelProfile(kernelProfile, {
kernelName: 'MatMul',
outputs: [resultScalar],
timeMs: queryTimeMs,
inputs,
extraInfo,
}).then(() => doneFn());
}, delayMs * 2);
});
it('profiles nested kernel with optional inputs', doneFn => {
const delayMs = 5;
const queryTimeMs = 10;
const inputs = { 'x': tf.tensor1d([1]), 'bias': null };
const extraInfo = '';
const timer = new TestBackendTimer(delayMs, queryTimeMs, extraInfo);
const logger = new TestLogger();
const profiler = new Profiler(timer, logger);
spyOn(timer, 'time').and.callThrough();
spyOn(logger, 'logKernelProfile').and.callThrough();
const timeSpy = timer.time;
let matmulKernelCalled = false;
let maxKernelCalled = false;
const result = 1;
const resultScalar = tf.scalar(result);
let innerKernelProfile;
const outerKernelProfile = profiler.profileKernel('MatMul', inputs, () => {
innerKernelProfile = profiler.profileKernel('Max', inputs, () => {
maxKernelCalled = true;
return [resultScalar];
});
matmulKernelCalled = true;
return innerKernelProfile.outputs;
});
setTimeout(() => {
expect(timeSpy.calls.count()).toBe(2);
expect(matmulKernelCalled).toBe(true);
expect(maxKernelCalled).toBe(true);
const checkInnerKernelProfile = checkKernelProfile(innerKernelProfile, {
kernelName: 'Max',
outputs: [resultScalar],
timeMs: queryTimeMs,
inputs,
extraInfo
});
const checkOuterKernelProfile = checkKernelProfile(outerKernelProfile, {
kernelName: 'MatMul',
outputs: [resultScalar],
timeMs: queryTimeMs * 2,
inputs,
extraInfo
});
Promise.all([checkInnerKernelProfile, checkOuterKernelProfile])
.then(() => doneFn());
}, delayMs * 2);
});
it('log kernelProfile', doneFn => {
const delayMs = 5;
const queryTimeMs = 10;
const inputs = { 'x': tf.tensor1d([1]) };
const extraInfo = '';
const timer = new TestBackendTimer(delayMs, queryTimeMs, extraInfo);
const logger = new TestLogger();
const profiler = new Profiler(timer, logger);
spyOn(logger, 'logKernelProfile').and.callThrough();
const logKernelProfileSpy = logger.logKernelProfile;
const result = 1;
const resultScalar = tf.scalar(result);
const kernelProfiles = profiler.profileKernel('MatMul', inputs, () => {
return [resultScalar];
});
profiler.logKernelProfile(kernelProfiles);
setTimeout(() => {
expect(logKernelProfileSpy.calls.count()).toBe(1);
expect(logKernelProfileSpy.calls.first().args).toEqual([
'MatMul', resultScalar, new Float32Array([result]), queryTimeMs, inputs,
extraInfo
]);
doneFn();
}, delayMs * 2);
});
});
describe('profiler.checkComputationForErrors', () => {
beforeAll(() => {
// Silence warnings.
spyOn(console, 'warn');
});
it('Float32Array has NaN', () => {
expect(checkComputationForErrors(new Float32Array([1, 2, 3, NaN, 4, 255]), 'float32', 'test'))
.toBe(true);
});
it('Float32Array has Infinity', () => {
expect(checkComputationForErrors(new Float32Array([1, 2, 3, Infinity, 4, 255]), 'float32', 'test'))
.toBe(true);
});
it('Float32Array no NaN', () => {
// Int32 and Bool NaNs should not trigger an error.
expect(checkComputationForErrors(new Float32Array([1, 2, 3, -1, 4, 255]), 'float32', 'test'))
.toBe(false);
});
});
describeWithFlags('profiler.Logger', ALL_ENVS, () => {
it('skips logging for undefined input node in input tensor map', () => {
const kernelName = 'FusedConv2D';
const vals = new Float32Array(1);
const outputs = tf.tensor1d([1]);
const timeMs = 10;
const inputs = {
'x': tf.tensor1d([1]),
'filter': tf.tensor1d([1]),
'bias': tf.tensor1d([1]),
'preluActivationWeights': undefined
};
const extraInfo = '';
const logger = new Logger();
spyOn(console, 'log');
const consoleLogSpy = console.log;
logger.logKernelProfile(kernelName, outputs, vals, timeMs, inputs, extraInfo);
expect(consoleLogSpy.calls.first().args)
.not.toContain('preluActivationWeights');
});
});
//# sourceMappingURL=profiler_test.js.map