Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,12 @@ jobs:
run: python test/python/library_integration.py --suite all

codec-suite:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
if: >-
github.event_name == 'schedule' ||
github.event_name == 'workflow_dispatch' ||
github.event_name == 'push' ||
(github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.name, 'area:codec'))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
6 changes: 6 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,12 @@ export TYWRAP_TORCH_ALLOW_COPY="1"
# Performance tuning
export TYWRAP_CACHE_DIR="./.tywrap/cache"
export TYWRAP_MEMORY_LIMIT="1024"
export TYWRAP_PERF_BUDGETS="1"
export TYWRAP_PERF_TIME_BUDGET_MS="2000"
export TYWRAP_PERF_MEMORY_BUDGET_MB="64"
export TYWRAP_CODEC_PERF_ITERATIONS="200"
export TYWRAP_CODEC_PERF_TIME_BUDGET_MS="500"
export TYWRAP_CODEC_PERF_MEMORY_BUDGET_MB="32"

# Development
export TYWRAP_VERBOSE="true"
Expand Down
85 changes: 85 additions & 0 deletions test/codec-performance.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { describe, it, expect } from 'vitest';
import { decodeValue } from '../src/utils/codec.js';
import { isNodejs } from '../src/utils/runtime.js';

const shouldRun = isNodejs() && (process.env.CI || process.env.TYWRAP_PERF_BUDGETS === '1');
const describeBudget = shouldRun ? describe : describe.skip;

const readEnvNumber = (name: string, fallback: string): number =>
Number(process.env[name] ?? fallback);

const runGc = (): void => {
if (global.gc) {
global.gc();
}
};

describeBudget('Codec performance budgets', () => {
it('decodes representative envelopes within time/memory budgets', () => {
const iterations = readEnvNumber('TYWRAP_CODEC_PERF_ITERATIONS', '200');
const timeBudgetMs = readEnvNumber('TYWRAP_CODEC_PERF_TIME_BUDGET_MS', '500');
const memoryBudgetMb = readEnvNumber('TYWRAP_CODEC_PERF_MEMORY_BUDGET_MB', '32');
const memoryBudgetBytes = memoryBudgetMb * 1024 * 1024;

const sparseEnvelope = {
__tywrap__: 'scipy.sparse',
codecVersion: 1,
encoding: 'json',
format: 'csr',
shape: [100, 100],
data: Array.from({ length: 200 }, (_, idx) => idx % 7),
indices: Array.from({ length: 200 }, (_, idx) => idx % 100),
indptr: Array.from({ length: 101 }, (_, idx) => Math.min(idx * 2, 200)),
} as const;

const torchEnvelope = {
__tywrap__: 'torch.tensor',
codecVersion: 1,
encoding: 'ndarray',
value: {
__tywrap__: 'ndarray',
codecVersion: 1,
encoding: 'json',
data: [
[1, 2],
[3, 4],
],
shape: [2, 2],
},
shape: [2, 2],
dtype: 'float32',
device: 'cpu',
} as const;

const sklearnEnvelope = {
__tywrap__: 'sklearn.estimator',
codecVersion: 1,
encoding: 'json',
className: 'LinearRegression',
module: 'sklearn.linear_model._base',
version: '1.4.2',
params: {
fit_intercept: true,
copy_X: true,
},
} as const;

runGc();
const startMem = process.memoryUsage().heapUsed;
const start = performance.now();

for (let i = 0; i < iterations; i += 1) {
decodeValue(sparseEnvelope);
decodeValue(torchEnvelope);
decodeValue(sklearnEnvelope);
}

const duration = performance.now() - start;
runGc();
const endMem = process.memoryUsage().heapUsed;
const deltaMem = endMem - startMem;

expect(duration).toBeLessThan(timeBudgetMs);
expect(deltaMem).toBeLessThan(memoryBudgetBytes);
});
});
Loading