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

Add timeout to benchmark solution runs #977

Merged
merged 1 commit into from
Sep 14, 2024
Merged
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
11 changes: 11 additions & 0 deletions BENCHMARK.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Some solutions are not included in the automated benchmark runs, either because
- [Running a benchmark of all solutions for a particular language](#running-a-benchmark-of-all-solutions-for-a-particular-language)
- [Running in unconfined mode](#running-in-unconfined-mode)
- [Output formats](#output-formats)
- [Setting the solution timeout](#setting-the-solution-timeout)

## What operating system to use?

Expand Down Expand Up @@ -375,3 +376,13 @@ The output format can be controlled via the `FORMATTER` variable like this:
make FORMATTER=json
make DIRECTORY=PrimeCrystal/solution_1 FORMATTER=csv
```

## Setting the solution timeout

The run of each solution is limited to a certain duration, which is 10 minutes by default.
You can change this setting through the `TIMEOUT` variable like this:

```shell
make TIMEOUT=15
make DIRECTORY=PrimeCPP/solution_2 TIMEOUT=15
```
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ SHELL := /bin/bash

DIRECTORY := $(shell pwd)
FORMATTER := "table"
TIMEOUT := "10"

.PHONY: all
all: benchmark
Expand All @@ -14,6 +15,7 @@ benchmark: check-env
ARGS=("-d $${REALPATH}" "-f $(FORMATTER)"); \
[ ! -z $${OUTPUT_FILE} ] && ARGS+=( "-o $${OUTPUT_FILE}" ); \
[ ! -z $${UNCONFINED} ] && ARGS+=( "--unconfined" ); \
[ ! -z $${TIMEOUT} ] && ARGS+=( "-t $${TIMEOUT}" ); \
cd ./tools; npm ci --silent && npm start --silent -- benchmark $${ARGS[@]}

.PHONY: check-env
Expand Down
17 changes: 13 additions & 4 deletions tools/src/commands/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ export const command = new Command('benchmark')
.option('-f, --formatter <type>', 'Output formatter', 'table')
.option('-o, --output-file <file>', 'Write output to given file')
.option('-u, --unconfined', 'Run with seccomp:unconfined (native performance for interpreted languages)')
.option('-t, --timeout <minutes>', 'Timeout for each benchmark in minutes', '10')
.action(async (args) => {
const directory = path.resolve(args.directory as string);
const unconfined = args.unconfined === true;
const timeout = parseInt(args.timeout as string);

logger.info(`Unconfined mode: ${unconfined}`);

Expand Down Expand Up @@ -106,11 +108,18 @@ export const command = new Command('benchmark')
let output = '';
try {
logger.info(`[${implementation}][${solution}] Running...`);
output = dockerService.runContainer(imageName, options);
output = dockerService.runContainer(imageName, timeout, options);
} catch (err) {
logger.warn(
`[${implementation}][${solution}] Exited with abnormal code: ${err.status}. Results might be partial...`
);
if (err.signal) {
logger.warn(
`[${implementation}][${solution}] Killed after ${timeout} minutes with signal: ${err.signal}. Results are likely partial...`
);
}
else {
logger.warn(
`[${implementation}][${solution}] Exited with abnormal code: ${err.status}. Results might be partial...`
);
}
output = err.output
.filter((block: Buffer | null) => block !== null)
.map((block: Buffer) => block.toString('utf8'))
Expand Down
6 changes: 4 additions & 2 deletions tools/src/services/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ export default class DockerService {
});
}

public runContainer(imageName: string, options: Array<string>): string {
public runContainer(imageName: string, duration: number, options: Array<string>): string {
const output = child_process.execSync(`docker run --rm ${options.join(' ')} ${imageName}`, {
stdio: 'pipe'
stdio: 'pipe',
timeout: duration ? duration * 60000 : undefined,
killSignal: 'SIGKILL'
});
return output.toString('utf8');
}
Expand Down