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

Improve cache handling on the frontend, cache executions on the backend, and improve controls on the exec pane #5111

Merged
merged 15 commits into from
Jun 11, 2023
Merged
84 changes: 66 additions & 18 deletions lib/base-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ import * as PromClient from 'prom-client';
import temp from 'temp';
import _ from 'underscore';

import type {
import {
BufferOkFunc,
BuildResult,
BuildStep,
BypassCacheControl,
CompilationCacheKey,
CompilationInfo,
CompilationResult,
Expand Down Expand Up @@ -475,7 +476,7 @@ export class BaseCompiler implements ICompiler {
maxSize: number,
intelAsm,
demangle,
staticReloc: boolean,
staticReloc: boolean | undefined,
dynamicReloc: boolean,
filters: ParseFiltersAndOutputOptions,
) {
Expand Down Expand Up @@ -1695,11 +1696,13 @@ export class BaseCompiler implements ICompiler {
};
}

async getOrBuildExecutable(key) {
async getOrBuildExecutable(key, bypassCache?: BypassCacheControl) {
const dirPath = await this.newTempDir();

const buildResults = await this.loadPackageWithExecutable(key, dirPath);
if (buildResults) return buildResults;
if (!bypassCache || !(bypassCache & BypassCacheControl.Compilation)) {
jeremy-rifkin marked this conversation as resolved.
Show resolved Hide resolved
const buildResults = await this.loadPackageWithExecutable(key, dirPath);
if (buildResults) return buildResults;
}

let compilationResult;
try {
Expand Down Expand Up @@ -1820,18 +1823,37 @@ export class BaseCompiler implements ICompiler {
};
}

async handleExecution(key, executeParameters): Promise<CompilationResult> {
if (this.compiler.interpreted) return this.handleInterpreting(key, executeParameters);
const buildResult = await this.getOrBuildExecutable(key);
async handleExecution(key, executeParameters, bypassCache?: BypassCacheControl): Promise<CompilationResult> {
// stringify now so shallow copying isn't a problem, I think the executeParameters get modified
const execKey = JSON.stringify({key, executeParameters});
if (!bypassCache || !(bypassCache & BypassCacheControl.Execution)) {
const cacheResult = await this.env.cacheGet(execKey as any);
if (cacheResult) {
return cacheResult;
}
}

if (this.compiler.interpreted) {
const result = this.handleInterpreting(key, executeParameters);
if (!bypassCache || !(bypassCache & BypassCacheControl.Execution)) {
await this.env.cachePut(execKey, result, undefined);
}
return result;
}
const buildResult = await this.getOrBuildExecutable(key, bypassCache);
if (buildResult.code !== 0) {
return {
const result = {
code: -1,
didExecute: false,
buildResult,
stderr: [{text: 'Build failed'}],
stdout: [],
timedOut: false,
};
if (!bypassCache || !(bypassCache & BypassCacheControl.Execution)) {
await this.env.cachePut(execKey, result, undefined);
}
return result;
}

if (!(await utils.fileExists(buildResult.executableFilename))) {
Expand All @@ -1846,27 +1868,38 @@ export class BaseCompiler implements ICompiler {

verboseResult.buildResult.stderr.push({text: 'Compiler did not produce an executable'});

if (!bypassCache || !(bypassCache & BypassCacheControl.Execution)) {
await this.env.cachePut(execKey, verboseResult, undefined);
}
return verboseResult;
}

if (!this.compiler.supportsExecute) {
return {
const result = {
code: -1,
didExecute: false,
buildResult,
stderr: [{text: 'Compiler does not support execution'}],
stdout: [],
timedOut: false,
};
if (!bypassCache || !(bypassCache & BypassCacheControl.Execution)) {
await this.env.cachePut(execKey, result, undefined);
}
return result;
}

executeParameters.ldPath = this.getSharedLibraryPathsAsLdLibraryPathsForExecution(key.libraries);
const result = await this.runExecutable(buildResult.executableFilename, executeParameters, buildResult.dirPath);
return {
const fullResult = {
...result,
didExecute: true,
buildResult: buildResult,
};
if (!bypassCache || !(bypassCache & BypassCacheControl.Execution)) {
await this.env.cachePut(execKey, fullResult, undefined);
}
return fullResult;
}

getCacheKey(source, options, backendOptions, filters, tools, libraries, files) {
Expand Down Expand Up @@ -2239,7 +2272,7 @@ export class BaseCompiler implements ICompiler {
}
}

async cmake(files, key) {
async cmake(files, key, bypassCache: BypassCacheControl) {
// key = {source, options, backendOptions, filters, bypassCache, tools, executionParameters, libraries};

if (!this.compiler.supportsBinary) {
Expand Down Expand Up @@ -2277,7 +2310,9 @@ export class BaseCompiler implements ICompiler {

const outputFilename = this.getExecutableFilename(path.join(dirPath, 'build'), this.outputFilebase, key);

let fullResult = await this.loadPackageWithExecutable(cacheKey, dirPath);
let fullResult =
(!bypassCache || !(bypassCache & BypassCacheControl.Execution)) &&
(await this.loadPackageWithExecutable(cacheKey, dirPath));
if (fullResult) {
fullResult.fetchedFromCache = true;

Expand Down Expand Up @@ -2398,6 +2433,7 @@ export class BaseCompiler implements ICompiler {
cacheKey.filters,
libsAndOptions.options,
optOutput,
bypassCache ?? BypassCacheControl.None,
path.join(dirPath, 'build'),
);

Expand Down Expand Up @@ -2446,7 +2482,17 @@ export class BaseCompiler implements ICompiler {
}
}

async compile(source, options, backendOptions, filters, bypassCache, tools, executionParameters, libraries, files) {
async compile(
source,
options,
backendOptions,
filters,
bypassCache: BypassCacheControl,
tools,
executionParameters,
libraries,
files,
) {
const optionsError = this.checkOptions(options);
if (optionsError) throw optionsError;
const sourceError = this.checkSource(source);
Expand All @@ -2471,7 +2517,7 @@ export class BaseCompiler implements ICompiler {
filters = Object.assign({}, filters);
filters.execute = false;

if (!bypassCache) {
if (!(bypassCache & BypassCacheControl.Compilation)) {
const cacheRetreiveTimeStart = process.hrtime.bigint();
// TODO: We should be able to eliminate this any cast. `key` should be cacheable (if it's not that's a big
// problem) Because key coantains a CompilerInfo which contains a function member it can't be assigned to a
Expand All @@ -2487,7 +2533,7 @@ export class BaseCompiler implements ICompiler {
if (doExecute) {
result.execResult = await this.env.enqueue(async () => {
const start = performance.now();
const res = await this.handleExecution(key, executeParameters);
const res = await this.handleExecution(key, executeParameters, bypassCache);
executionTimeHistogram.observe((performance.now() - start) / 1000);
return res;
});
Expand All @@ -2506,7 +2552,7 @@ export class BaseCompiler implements ICompiler {
source = this.preProcess(source, filters);

if (backendOptions.executorRequest) {
const execResult = await this.handleExecution(key, executeParameters);
const execResult = await this.handleExecution(key, executeParameters, bypassCache);
if (execResult && execResult.buildResult) {
this.doTempfolderCleanup(execResult.buildResult);
}
Expand Down Expand Up @@ -2544,6 +2590,7 @@ export class BaseCompiler implements ICompiler {
filters,
options,
optOutput,
bypassCache,
);
})();
compilationTimeHistogram.observe((performance.now() - start) / 1000);
Expand All @@ -2561,10 +2608,11 @@ export class BaseCompiler implements ICompiler {
filters,
options,
optOutput,
bypassCache: BypassCacheControl,
customBuildPath?,
) {
// Start the execution as soon as we can, but only await it at the end.
const execPromise = doExecute ? this.handleExecution(key, executeParameters) : null;
const execPromise = doExecute ? this.handleExecution(key, executeParameters, bypassCache) : null;

if (result.hasOptOutput) {
delete result.optPath;
Expand Down
6 changes: 4 additions & 2 deletions lib/handlers/compile.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import {BypassCacheControl} from '../../types/compilation/compilation.interfaces.js';

// IF YOU MODIFY ANYTHING HERE PLEASE UPDATE THE DOCUMENTATION!

// This type models a request so all fields must be optional strings.
Expand Down Expand Up @@ -52,12 +54,12 @@ export type CompilationRequestArgs = {
export type CompileRequestJsonBody = {
options: CompilationRequestArgs;
source: string;
bypassCache: boolean;
bypassCache: BypassCacheControl;
};

export type CompileRequestTextBody = {
source: string;
bypassCache: boolean;
bypassCache: BypassCacheControl;
options: any;
userArguments: string;
executeParametersArgs: any;
Expand Down
22 changes: 11 additions & 11 deletions lib/handlers/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ import {
} from './compile.interfaces.js';
import {remove} from '../common-utils.js';
import {CompilerOverrideOptions} from '../../types/compilation/compiler-overrides.interfaces.js';
import {
BypassCacheControl,
CompileChildLibraries,
ExecutionParams,
} from '../../types/compilation/compilation.interfaces.js';

temp.track();

Expand Down Expand Up @@ -81,20 +86,15 @@ function initialise(compilerEnv: CompilationEnvironment) {
}, tempDirCleanupSecs * 1000);
}

export type ExecutionParams = {
args: string[];
stdin: string;
};

type ParsedRequest = {
source: string;
options: string[];
backendOptions: Record<string, any>;
filters: ParseFiltersAndOutputOptions;
bypassCache: boolean;
bypassCache: BypassCacheControl;
tools: any;
executionParameters: ExecutionParams;
libraries: any[];
libraries: CompileChildLibraries[];
jeremy-rifkin marked this conversation as resolved.
Show resolved Hide resolved
};

export class CompileHandler {
Expand Down Expand Up @@ -351,7 +351,7 @@ export class CompileHandler {
options: string,
backendOptions: Record<string, any> = {},
filters: ParseFiltersAndOutputOptions,
bypassCache = false,
bypassCache = BypassCacheControl.None,
tools;
const execReqParams: ExecutionRequestParams = {};
let libraries: any[] = [];
Expand All @@ -361,7 +361,7 @@ export class CompileHandler {
const jsonRequest = this.checkRequestRequirements(req);
const requestOptions = jsonRequest.options;
source = jsonRequest.source;
if (jsonRequest.bypassCache) bypassCache = true;
if (jsonRequest.bypassCache) bypassCache = jsonRequest.bypassCache;
options = requestOptions.userArguments;
const execParams = requestOptions.executeParameters || {};
execReqParams.args = execParams.args;
Expand All @@ -373,7 +373,7 @@ export class CompileHandler {
} else if (req.body && req.body.compiler) {
const textRequest = req.body as CompileRequestTextBody;
source = textRequest.source;
if (textRequest.bypassCache) bypassCache = true;
if (textRequest.bypassCache) bypassCache = textRequest.bypassCache;
options = textRequest.userArguments;
execReqParams.args = textRequest.executeParametersArgs;
execReqParams.stdin = textRequest.executeParametersStdin;
Expand Down Expand Up @@ -495,7 +495,7 @@ export class CompileHandler {
this.cmakeCounter.inc({language: compiler.lang.id});
const options = this.parseRequest(req, compiler);
compiler
.cmake(req.body.files, options)
.cmake(req.body.files, options, req.body.bypassCache)
.then(result => {
if (result.didExecute || (result.execResult && result.execResult.didExecute))
this.cmakeExecuteCounter.inc({language: compiler.lang.id});
Expand Down
6 changes: 3 additions & 3 deletions static/compiler-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const ASCII_COLORS_RE = new RegExp(/\x1B\[[\d;]*m(.\[K)?/g);
export class CompilerService {
private readonly base = window.httpRoot;
private allowStoreCodeDebug: boolean;
cache: LRU<string, CompilationResult>;
private cache: LRU<string, CompilationResult>;
private readonly compilersByLang: Record<string, Record<string, CompilerInfo>>;

constructor(eventHub: EventEmitter) {
Expand Down Expand Up @@ -214,7 +214,7 @@ export class CompilerService {
public async submit(request: Record<string, any>) {
request.allowStoreCodeDebug = this.allowStoreCodeDebug;
const jsonRequest = JSON.stringify(request);
if (options.doCache) {
if (options.doCache && !request.bypassCache) {
const cachedResult = this.cache.get(jsonRequest);
if (cachedResult) {
return {
Expand Down Expand Up @@ -252,7 +252,7 @@ export class CompilerService {
public submitCMake(request: Record<string, any>) {
request.allowStoreCodeDebug = this.allowStoreCodeDebug;
const jsonRequest = JSON.stringify(request);
if (options.doCache) {
if (options.doCache && !request.bypassCache) {
const cachedResult = this.cache.get(jsonRequest);
if (cachedResult) {
return Promise.resolve({
Expand Down
Loading
Loading