fm-bench 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/fm-bench.js CHANGED
@@ -4,18 +4,29 @@ import { killActiveChildren } from '../src/process.js';
4
4
 
5
5
  // Terminate any in-flight `fm` child process before exiting, then use the
6
6
  // conventional 128 + signal exit code. A second signal exits immediately.
7
- let interrupting = false;
7
+ let signalsReceived = 0;
8
+ let interrupted = false;
8
9
  for (const [signal, exitCode] of [['SIGINT', 130], ['SIGTERM', 143]]) {
9
10
  process.on(signal, () => {
11
+ signalsReceived += 1;
12
+ interrupted = true;
13
+ // Carry the status on the process too, so the code survives even if the
14
+ // exit below is preempted.
15
+ process.exitCode = exitCode;
10
16
  const killed = killActiveChildren(signal);
11
- if (interrupting) process.exit(exitCode);
12
- interrupting = true;
13
- const graceMs = killed > 0 ? 500 : 0;
14
- setTimeout(() => process.exit(exitCode), graceMs).unref();
17
+ if (signalsReceived > 1 || killed === 0) process.exit(exitCode);
18
+ // Deliberately not unref'd: this timer performs the exit after giving the
19
+ // killed children a moment to be reaped.
20
+ setTimeout(() => process.exit(exitCode), 500);
15
21
  });
16
22
  }
17
23
 
18
24
  runCli(process.argv.slice(2)).catch((error) => {
25
+ if (interrupted) {
26
+ // The user asked to stop; a follow-up benchmark error is noise.
27
+ process.exitCode = typeof error?.exitCode === 'number' ? error.exitCode : 1;
28
+ return;
29
+ }
19
30
  const message = error?.message || String(error);
20
31
  console.error(`fm-bench: ${message}`);
21
32
  process.exitCode = typeof error?.exitCode === 'number' ? error.exitCode : 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fm-bench",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Dynamic benchmark CLI for Apple's fm command on macOS 27+.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,9 +26,15 @@
26
26
  },
27
27
  "keywords": [
28
28
  "apple",
29
+ "apple-intelligence",
30
+ "apple-silicon",
29
31
  "foundation-models",
30
32
  "fm",
33
+ "llm",
31
34
  "benchmark",
35
+ "benchmarking",
36
+ "latency",
37
+ "throughput",
32
38
  "macos",
33
39
  "cli"
34
40
  ],
package/src/bench.js CHANGED
@@ -381,13 +381,22 @@ async function runSingleBenchmark(context) {
381
381
 
382
382
  async function runLimited(items, concurrency, worker, options = {}) {
383
383
  let nextIndex = 0;
384
+ // fail-fast stops admitting new work; calls already in flight are allowed to
385
+ // finish so no `fm` process is abandoned mid-run.
386
+ let cancelled = false;
384
387
  const waitForSlot = createPacer(options.requestRate, options.rampUpMs);
385
388
  const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
386
- while (nextIndex < items.length) {
389
+ while (!cancelled && nextIndex < items.length) {
387
390
  const index = nextIndex;
388
391
  nextIndex += 1;
389
392
  await waitForSlot(index);
390
- await worker(items[index]);
393
+ if (cancelled) break;
394
+ try {
395
+ await worker(items[index]);
396
+ } catch (error) {
397
+ cancelled = true;
398
+ throw error;
399
+ }
391
400
  }
392
401
  });
393
402
  await Promise.all(workers);