Skip to content

Commit b2130f2

Browse files
Added 'per-file-time-limit' cli option (#1414)
feat(benchmark): add 'per-file-time-limit' cli option
1 parent 14db9d7 commit b2130f2

File tree

4 files changed

+25
-9
lines changed

4 files changed

+25
-9
lines changed

src/cli/benchmark-app.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface BenchmarkCliOptions {
2121
'enable-pointer-tracking': boolean
2222
'max-file-slices': number
2323
threshold?: number
24+
'per-file-time-limit'?: number
2425
}
2526

2627
const options = processCommandLineArgs<BenchmarkCliOptions>('benchmark', [],{
@@ -107,7 +108,8 @@ async function benchmark() {
107108
// we reverse here "for looks", since the helper pops from the end, and we want file ids to be ascending :D
108109
args.map(a => [...a, '--run-num', `${i}`]).reverse(),
109110
limit,
110-
options.parallel
111+
options.parallel,
112+
options['per-file-time-limit']
111113
);
112114
await pool.run();
113115
const stats = pool.getStats();

src/cli/common/options.ts

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const benchmarkOptions: OptionDefinition[] = [
3434
{ name: 'enable-pointer-tracking', type: Boolean, description: 'Run dataflow analysis with pointer tracking', defaultValue: false },
3535
{ name: 'max-file-slices', type: Number, description: 'If file has more than passed number of slices, the file is not processed', defaultValue: -1, typeLabel: '{underline number}' },
3636
{ name: 'threshold', alias: 't', type: Number, description: 'How many re-visits of the same node are ok?', defaultValue: undefined, typeLabel: '{underline number}' },
37+
{ name: 'per-file-time-limit', type: Number, description: 'Time limit in milliseconds to process single file (disabled by default)', defaultValue: undefined, typeLabel: '{underline number}' },
3738
];
3839

3940
export const benchmarkHelperOptions: OptionDefinition[] = [

src/dataflow/environments/overwrite.ts

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { REnvironmentInformation, IEnvironment } from './environment';
33
import { BuiltInEnvironment , Environment } from './environment';
44
import type { IdentifierDefinition } from './identifier';
55
import type { ControlDependency } from '../info';
6+
import { log } from '../../util/log';
67

78
function anyIsMaybeOrEmpty(values: readonly IdentifierDefinition[]): boolean {
89
if(values.length === 0) {
@@ -20,6 +21,9 @@ export function overwriteIEnvironmentWith(base: IEnvironment | undefined, next:
2021
guard(base !== undefined && next !== undefined, 'can not overwrite environments with undefined');
2122
const map = new Map(base.memory);
2223
for(const [key, values] of next.memory) {
24+
if(values.length > 1_000_000) {
25+
log.warn(`Overwriting environment with ${values.length} definitions for ${key}`);
26+
}
2327
const hasMaybe = applyCds?.length === 0 || applyCds !== undefined ? true : anyIsMaybeOrEmpty(values);
2428
if(hasMaybe) {
2529
const old = map.get(key);

src/util/parallel.ts

+17-8
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,27 @@ type WorkingQueue = Arguments[]
1616
* It offers a work stealing thread pool executor.
1717
*/
1818
export class LimitedThreadPool {
19-
private readonly workingQueue: WorkingQueue;
20-
private readonly limit: number;
21-
private readonly parallel: number;
22-
private readonly module: string;
19+
private readonly workingQueue: WorkingQueue;
20+
private readonly limit: number;
21+
private readonly parallel: number;
22+
private readonly module: string;
2323
private counter = 0;
24-
private skipped: Arguments[] = [];
24+
private skipped: Arguments[] = [];
2525
private currentlyRunning = new Set<Arguments>();
26-
private reportingInterval: NodeJS.Timer | undefined = undefined;
26+
private reportingInterval: NodeJS.Timer | undefined = undefined;
27+
private readonly timeLimitInMs: number | undefined;
2728

2829
/**
2930
* Create a new parallel helper that runs the given `module` once for each list of {@link Arguments} in the `queue`.
3031
* The `limit` stops the execution if `<limit>` number of runs exited successfully.
3132
* The `parallel` parameter limits the number of parallel executions.
3233
*/
33-
constructor(module: string, queue: WorkingQueue, limit: number, parallel: number) {
34+
constructor(module: string, queue: WorkingQueue, limit: number, parallel: number, timeLimitInMs?: number) {
3435
this.workingQueue = queue;
3536
this.limit = limit;
3637
this.module = module;
3738
this.parallel = parallel;
39+
this.timeLimitInMs = timeLimitInMs;
3840
}
3941

4042
public async run(): Promise<void> {
@@ -69,9 +71,16 @@ export class LimitedThreadPool {
6971

7072
const child = cp.fork(this.module, args);
7173
child.on('exit', this.onChildExit(args));
74+
let timeout: NodeJS.Timeout | undefined;
75+
if(this.timeLimitInMs) {
76+
timeout = setTimeout(() => {
77+
log.error(`Killing child process with '${JSON.stringify(args)}' after ${this.timeLimitInMs}ms`);
78+
child.kill();
79+
}, this.timeLimitInMs);
80+
}
7281

7382
// schedule re-schedule
74-
await new Promise<void>(resolve => child.on('exit', resolve)).then(() => this.runNext());
83+
await new Promise<void>(resolve => child.on('exit', resolve)).then(() => clearTimeout(timeout)).then(() => this.runNext());
7584
}
7685

7786

0 commit comments

Comments
 (0)