ic-mops 0.35.0 → 0.36.1

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/cli.ts CHANGED
@@ -218,9 +218,10 @@ program
218
218
  program
219
219
  .command('bench [filter]')
220
220
  .description('Run benchmarks')
221
+ .addOption(new Option('--replica <replica>', 'Which replica to use to run benchmarks').choices(['dfx', 'pocket-ic']).default('dfx'))
222
+ .addOption(new Option('--gc <gc>', 'Garbage collector').choices(['copying', 'compacting', 'generational', 'incremental']).default('copying'))
221
223
  .addOption(new Option('--save', 'Save benchmark results to .bench/<filename>.json'))
222
224
  .addOption(new Option('--compare', 'Run benchmark and compare results with .bench/<filename>.json'))
223
- .addOption(new Option('--gc <gc>', 'Garbage collector').choices(['copying', 'compacting', 'generational', 'incremental']).default('incremental'))
224
225
  // .addOption(new Option('--force-gc', 'Force GC'))
225
226
  .addOption(new Option('--verbose', 'Show more information'))
226
227
  .action(async (filter, options) => {
@@ -0,0 +1,99 @@
1
+ import {execSync} from 'node:child_process';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import {execaCommand} from 'execa';
5
+ import {PocketIc} from '@hadronous/pic';
6
+ import {getRootDir} from '../mops.js';
7
+ import {createActor, idlFactory} from '../declarations/bench/index.js';
8
+
9
+ export class BenchReplica {
10
+ type : 'dfx' | 'pocket-ic';
11
+ verbose = false;
12
+ canisters: Record<string, {cwd: string; canisterId: string; actor: any;}> = {};
13
+ pocketIc?: PocketIc;
14
+
15
+ constructor(type: 'dfx' | 'pocket-ic', verbose = false) {
16
+ this.type = type;
17
+ this.verbose = verbose;
18
+ }
19
+
20
+ async start() {
21
+ console.log(`Starting ${this.type} replica...`);
22
+
23
+ if (this.type == 'dfx') {
24
+ await this.stop();
25
+ let dir = path.join(getRootDir(), '.mops/.bench');
26
+ fs.writeFileSync(path.join(dir, 'dfx.json'), JSON.stringify(this.dfxJson(''), null, 2));
27
+ execSync('dfx start --background --clean --artificial-delay 0' + (this.verbose ? '' : ' -qqqq'), {cwd: dir, stdio: ['inherit', this.verbose ? 'inherit' : 'ignore', 'inherit']});
28
+ }
29
+ else {
30
+ this.pocketIc = await PocketIc.create();
31
+ }
32
+ }
33
+
34
+ async stop() {
35
+ if (this.type == 'dfx') {
36
+ let dir = path.join(getRootDir(), '.mops/.bench');
37
+ execSync('dfx stop' + (this.verbose ? '' : ' -qqqq'), {cwd: dir, stdio: ['pipe', this.verbose ? 'inherit' : 'ignore', 'pipe']});
38
+ }
39
+ else if (this.pocketIc) {
40
+ this.pocketIc.tearDown();
41
+ }
42
+ }
43
+
44
+ async deploy(name: string, wasm: string, cwd: string = process.cwd()) {
45
+ if (this.type === 'dfx') {
46
+ await execaCommand(`dfx deploy ${name} --mode reinstall --yes --identity anonymous`, {cwd, stdio: this.verbose ? 'pipe' : ['pipe', 'ignore', 'pipe']});
47
+ let canisterId = execSync(`dfx canister id ${name}`, {cwd}).toString().trim();
48
+ let actor = await createActor(canisterId, {
49
+ agentOptions: {
50
+ host: 'http://127.0.0.1:4944',
51
+ },
52
+ });
53
+ this.canisters[name] = {cwd, canisterId, actor};
54
+ }
55
+ else if (this.pocketIc) {
56
+ let {canisterId, actor} = await this.pocketIc.setupCanister(idlFactory, wasm);
57
+ this.canisters[name] = {
58
+ cwd,
59
+ canisterId: canisterId.toText(),
60
+ actor,
61
+ };
62
+ }
63
+ }
64
+
65
+ getActor(name: string): unknown {
66
+ return this.canisters[name]?.actor;
67
+ }
68
+
69
+ getCanisterId(name: string): string {
70
+ return this.canisters[name]?.canisterId || '';
71
+ }
72
+
73
+ dfxJson(canisterName: string) {
74
+ let canisters: Record<string, any> = {};
75
+ if (canisterName) {
76
+ canisters[canisterName] = {
77
+ type: 'custom',
78
+ wasm: 'canister.wasm',
79
+ candid: 'canister.did',
80
+ };
81
+ }
82
+
83
+ return {
84
+ version: 1,
85
+ canisters,
86
+ defaults: {
87
+ build: {
88
+ packtool: 'mops sources',
89
+ },
90
+ },
91
+ networks: {
92
+ local: {
93
+ type: 'ephemeral',
94
+ bind: '127.0.0.1:4944',
95
+ },
96
+ },
97
+ };
98
+ }
99
+ }
package/commands/bench.ts CHANGED
@@ -1,4 +1,3 @@
1
- import {execSync} from 'node:child_process';
2
1
  import path from 'node:path';
3
2
  import fs from 'node:fs';
4
3
  import os from 'node:os';
@@ -6,17 +5,19 @@ import chalk from 'chalk';
6
5
  import {globSync} from 'glob';
7
6
  import {markdownTable} from 'markdown-table';
8
7
  import logUpdate from 'log-update';
8
+ import {execaCommand} from 'execa';
9
+ import stringWidth from 'string-width';
9
10
 
10
11
  import {getRootDir} from '../mops.js';
11
12
  import {parallel} from '../parallel.js';
12
- import {createActor} from '../declarations/bench/index.js';
13
- import {BenchResult, BenchSchema, _SERVICE} from '../declarations/bench/bench.did.js';
14
13
  import {absToRel} from './test/utils.js';
15
14
  import {getMocVersion} from '../helpers/get-moc-version.js';
16
15
  import {getDfxVersion} from '../helpers/get-dfx-version.js';
17
16
  import {getMocPath} from '../helpers/get-moc-path.js';
18
17
  import {sources} from './sources.js';
19
- import {execaCommand} from 'execa';
18
+
19
+ import {BenchResult, BenchSchema, _SERVICE} from '../declarations/bench/bench.did.js';
20
+ import {BenchReplica} from './bench-replica.js';
20
21
 
21
22
  let ignore = [
22
23
  '**/node_modules/**',
@@ -31,30 +32,36 @@ let globConfig = {
31
32
  };
32
33
 
33
34
  type BenchOptions = {
34
- dfx?: string,
35
- moc?: string,
36
- gc?: 'copying' | 'compacting' | 'generational' | 'incremental',
37
- forceGc?: boolean,
38
- save?: boolean,
39
- compare?: boolean,
40
- verbose?: boolean,
35
+ replica: 'dfx' | 'pocket-ic',
36
+ replicaVersion: string,
37
+ moc: string,
38
+ gc: 'copying' | 'compacting' | 'generational' | 'incremental',
39
+ forceGc: boolean,
40
+ save: boolean,
41
+ compare: boolean,
42
+ verbose: boolean,
41
43
  };
42
44
 
43
- export async function bench(filter = '', options: BenchOptions = {}): Promise<boolean> {
45
+ export async function bench(filter = '', optionsArg: Partial<BenchOptions> = {}): Promise<boolean> {
44
46
  let defaultOptions: BenchOptions = {
47
+ replica: 'dfx',
45
48
  moc: getMocVersion(),
46
- dfx: getDfxVersion(),
47
- gc: 'incremental',
49
+ replicaVersion: '0.0.0',
50
+ gc: 'copying',
48
51
  forceGc: true,
49
52
  save: false,
50
53
  compare: false,
51
54
  verbose: false,
52
55
  };
53
56
 
54
- options = {...defaultOptions, ...options};
57
+ let options: BenchOptions = {...defaultOptions, ...optionsArg};
58
+
59
+ options.replicaVersion = options.replica == 'dfx' ? getDfxVersion() : '1.0.0';
55
60
 
56
61
  options.verbose && console.log(options);
57
62
 
63
+ let replica = new BenchReplica(options.replica, options.verbose);
64
+
58
65
  let rootDir = getRootDir();
59
66
  let globStr = '**/bench?(mark)/**/*.bench.mo';
60
67
  if (filter) {
@@ -85,17 +92,16 @@ export async function bench(filter = '', options: BenchOptions = {}): Promise<bo
85
92
  console.log('='.repeat(50));
86
93
  console.log('');
87
94
 
88
- console.log('Starting dfx replica...');
89
- startDfx(options.verbose);
95
+ await replica.start();
90
96
 
91
97
  console.log('Deploying canisters...');
92
98
  await parallel(os.cpus().length, files, async (file: string) => {
93
99
  try {
94
- await deployBenchFile(file, options);
100
+ await deployBenchFile(file, options, replica);
95
101
  }
96
102
  catch (err) {
97
- console.error('Unexpected error. Stopping dfx replica...');
98
- stopDfx(options.verbose);
103
+ console.error('Unexpected error. Stopping replica...');
104
+ await replica.stop();
99
105
  throw err;
100
106
  }
101
107
  });
@@ -105,17 +111,17 @@ export async function bench(filter = '', options: BenchOptions = {}): Promise<bo
105
111
  console.log(`\nRunning ${chalk.gray(absToRel(file))}...`);
106
112
  console.log('');
107
113
  try {
108
- await runBenchFile(file, options);
114
+ await runBenchFile(file, options, replica);
109
115
  }
110
116
  catch (err) {
111
- console.error('Unexpected error. Stopping dfx replica...');
112
- stopDfx(options.verbose);
117
+ console.error('Unexpected error. Stopping replica...');
118
+ await replica.stop();
113
119
  throw err;
114
120
  }
115
121
  });
116
122
 
117
- console.log('Stopping dfx replica...');
118
- stopDfx(options.verbose);
123
+ console.log('Stopping replica...');
124
+ await replica.stop();
119
125
 
120
126
  fs.rmSync(benchDir, {recursive: true, force: true});
121
127
 
@@ -133,55 +139,14 @@ function getMocArgs(options: BenchOptions): string {
133
139
  return args;
134
140
  }
135
141
 
136
- function dfxJson(canisterName: string, options: BenchOptions = {}) {
137
- options || console.log(options);
138
-
139
- let canisters: Record<string, any> = {};
140
- if (canisterName) {
141
- canisters[canisterName] = {
142
- type: 'custom',
143
- wasm: 'canister.wasm',
144
- candid: 'canister.did',
145
- };
146
- }
147
-
148
- return {
149
- version: 1,
150
- canisters,
151
- defaults: {
152
- build: {
153
- packtool: 'mops sources',
154
- },
155
- },
156
- networks: {
157
- local: {
158
- type: 'ephemeral',
159
- bind: '127.0.0.1:4944',
160
- },
161
- },
162
- };
163
- }
164
-
165
- function startDfx(verbose = false) {
166
- stopDfx(verbose);
167
- let dir = path.join(getRootDir(), '.mops/.bench');
168
- fs.writeFileSync(path.join(dir, 'dfx.json'), JSON.stringify(dfxJson(''), null, 2));
169
- execSync('dfx start --background --clean --artificial-delay 0' + (verbose ? '' : ' -qqqq'), {cwd: dir, stdio: ['inherit', verbose ? 'inherit' : 'ignore', 'inherit']});
170
- }
171
-
172
- function stopDfx(verbose = false) {
173
- let dir = path.join(getRootDir(), '.mops/.bench');
174
- execSync('dfx stop' + (verbose ? '' : ' -qqqq'), {cwd: dir, stdio: ['pipe', verbose ? 'inherit' : 'ignore', 'pipe']});
175
- }
176
-
177
- async function deployBenchFile(file: string, options: BenchOptions = {}): Promise<void> {
142
+ async function deployBenchFile(file: string, options: BenchOptions, replica: BenchReplica): Promise<void> {
178
143
  let rootDir = getRootDir();
179
144
  let tempDir = path.join(rootDir, '.mops/.bench/', path.parse(file).name);
180
145
  let canisterName = path.parse(file).name;
181
146
 
182
147
  // prepare temp files
183
148
  fs.mkdirSync(tempDir, {recursive: true});
184
- fs.writeFileSync(path.join(tempDir, 'dfx.json'), JSON.stringify(dfxJson(canisterName, options), null, 2));
149
+ fs.writeFileSync(path.join(tempDir, 'dfx.json'), JSON.stringify(replica.dfxJson(canisterName), null, 2));
185
150
 
186
151
  let benchCanisterData = fs.readFileSync(new URL('./bench/bench-canister.mo', import.meta.url), 'utf8');
187
152
  benchCanisterData = benchCanisterData.replace('./user-bench', path.relative(tempDir, file).replace(/.mo$/g, ''));
@@ -195,18 +160,15 @@ async function deployBenchFile(file: string, options: BenchOptions = {}): Promis
195
160
  options.verbose && console.timeEnd(`build ${canisterName}`);
196
161
 
197
162
  // deploy canister
163
+ let wasm = path.join(tempDir, 'canister.wasm');
198
164
  options.verbose && console.time(`deploy ${canisterName}`);
199
- await execaCommand(`dfx deploy ${canisterName} --mode reinstall --yes --identity anonymous`, {cwd: tempDir, stdio: options.verbose ? 'pipe' : ['pipe', 'ignore', 'pipe']});
165
+ // await execaCommand(`dfx deploy ${canisterName} --mode reinstall --yes --identity anonymous`, {cwd: tempDir, stdio: options.verbose ? 'pipe' : ['pipe', 'ignore', 'pipe']});
166
+ await replica.deploy(canisterName, wasm, tempDir);
200
167
  options.verbose && console.timeEnd(`deploy ${canisterName}`);
201
168
 
202
169
  // init bench
203
170
  options.verbose && console.time(`init ${canisterName}`);
204
- let canisterId = execSync(`dfx canister id ${canisterName}`, {cwd: tempDir}).toString().trim();
205
- let actor: _SERVICE = await createActor(canisterId, {
206
- agentOptions: {
207
- host: 'http://127.0.0.1:4944',
208
- },
209
- });
171
+ let actor = await replica.getActor(canisterName) as _SERVICE;
210
172
  await actor.init();
211
173
  options.verbose && console.timeEnd(`init ${canisterName}`);
212
174
  }
@@ -216,18 +178,11 @@ type RunBenchFileResult = {
216
178
  results: Map<string, BenchResult>,
217
179
  };
218
180
 
219
- async function runBenchFile(file: string, options: BenchOptions = {}): Promise<RunBenchFileResult> {
181
+ async function runBenchFile(file: string, options: BenchOptions, replica: BenchReplica): Promise<RunBenchFileResult> {
220
182
  let rootDir = getRootDir();
221
- let tempDir = path.join(rootDir, '.mops/.bench/', path.parse(file).name);
222
183
  let canisterName = path.parse(file).name;
223
184
 
224
- let canisterId = execSync(`dfx canister id ${canisterName}`, {cwd: tempDir}).toString().trim();
225
- let actor: _SERVICE = await createActor(canisterId, {
226
- agentOptions: {
227
- host: 'http://127.0.0.1:4944',
228
- },
229
- });
230
-
185
+ let actor = await replica.getActor(canisterName) as _SERVICE;
231
186
  let schema = await actor.getSchema();
232
187
 
233
188
  // load previous results
@@ -267,8 +222,8 @@ async function runBenchFile(file: string, options: BenchOptions = {}): Promise<R
267
222
  let percent = (Number(res[prop]) - Number(prevRes[prop])) / Number(prevRes[prop]) * 100;
268
223
  let sign = percent > 0 ? '+' : '';
269
224
  let percentText = percent == 0 ? '0%' : sign + percent.toFixed(2) + '%';
270
- // diff = ' (' + (percent > 0 ? chalk.red(percentText) : chalk.green(percentText)) + ')'; // alignment is broken
271
- diff = ' (' + percentText + ')';
225
+ let color: keyof typeof chalk = percent == 0 ? 'gray' : (percent > 0 ? 'red' : 'green');
226
+ diff = ` (${chalk[color](percentText)})`;
272
227
  }
273
228
  else {
274
229
  diff = chalk.yellow(' (no previous results)');
@@ -285,7 +240,10 @@ async function runBenchFile(file: string, options: BenchOptions = {}): Promise<R
285
240
  resArr.push(curRow);
286
241
  }
287
242
 
288
- return markdownTable(resArr, {align: ['l', ...'r'.repeat(schema.cols.length)]});
243
+ return markdownTable(resArr, {
244
+ align: ['l', ...'r'.repeat(schema.cols.length)],
245
+ stringLength: stringWidth,
246
+ });
289
247
  };
290
248
 
291
249
  let printResults = () => {
@@ -297,7 +255,9 @@ async function runBenchFile(file: string, options: BenchOptions = {}): Promise<R
297
255
  `);
298
256
  };
299
257
 
300
- printResults();
258
+ if (!process.env.CI) {
259
+ printResults();
260
+ }
301
261
 
302
262
  // run all cells
303
263
  for (let [rowIndex, row] of schema.rows.entries()) {
@@ -306,9 +266,15 @@ async function runBenchFile(file: string, options: BenchOptions = {}): Promise<R
306
266
  // let res = await actor.runCellUpdate(BigInt(rowIndex), BigInt(colIndex));
307
267
  let res = await actor.runCellUpdateAwait(BigInt(rowIndex), BigInt(colIndex));
308
268
  results.set(`${row}:${col}`, res);
309
- printResults();
269
+ if (!process.env.CI) {
270
+ printResults();
271
+ }
310
272
  }
311
273
  }
274
+
275
+ if (process.env.CI) {
276
+ printResults();
277
+ }
312
278
  logUpdate.done();
313
279
 
314
280
  // save results
@@ -317,7 +283,8 @@ async function runBenchFile(file: string, options: BenchOptions = {}): Promise<R
317
283
  let json: Record<any, any> = {
318
284
  version: 1,
319
285
  moc: options.moc,
320
- dfx: options.dfx,
286
+ replica: options.replica,
287
+ replicaVersion: options.replicaVersion,
321
288
  gc: options.gc,
322
289
  forceGc: options.forceGc,
323
290
  results: Array.from(results.entries()),
package/dist/cli.js CHANGED
@@ -190,9 +190,10 @@ program
190
190
  program
191
191
  .command('bench [filter]')
192
192
  .description('Run benchmarks')
193
+ .addOption(new Option('--replica <replica>', 'Which replica to use to run benchmarks').choices(['dfx', 'pocket-ic']).default('dfx'))
194
+ .addOption(new Option('--gc <gc>', 'Garbage collector').choices(['copying', 'compacting', 'generational', 'incremental']).default('copying'))
193
195
  .addOption(new Option('--save', 'Save benchmark results to .bench/<filename>.json'))
194
196
  .addOption(new Option('--compare', 'Run benchmark and compare results with .bench/<filename>.json'))
195
- .addOption(new Option('--gc <gc>', 'Garbage collector').choices(['copying', 'compacting', 'generational', 'incremental']).default('incremental'))
196
197
  // .addOption(new Option('--force-gc', 'Force GC'))
197
198
  .addOption(new Option('--verbose', 'Show more information'))
198
199
  .action(async (filter, options) => {
@@ -0,0 +1,32 @@
1
+ import { PocketIc } from '@hadronous/pic';
2
+ export declare class BenchReplica {
3
+ type: 'dfx' | 'pocket-ic';
4
+ verbose: boolean;
5
+ canisters: Record<string, {
6
+ cwd: string;
7
+ canisterId: string;
8
+ actor: any;
9
+ }>;
10
+ pocketIc?: PocketIc;
11
+ constructor(type: 'dfx' | 'pocket-ic', verbose?: boolean);
12
+ start(): Promise<void>;
13
+ stop(): Promise<void>;
14
+ deploy(name: string, wasm: string, cwd?: string): Promise<void>;
15
+ getActor(name: string): unknown;
16
+ getCanisterId(name: string): string;
17
+ dfxJson(canisterName: string): {
18
+ version: number;
19
+ canisters: Record<string, any>;
20
+ defaults: {
21
+ build: {
22
+ packtool: string;
23
+ };
24
+ };
25
+ networks: {
26
+ local: {
27
+ type: string;
28
+ bind: string;
29
+ };
30
+ };
31
+ };
32
+ }
@@ -0,0 +1,87 @@
1
+ import { execSync } from 'node:child_process';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import { execaCommand } from 'execa';
5
+ import { PocketIc } from '@hadronous/pic';
6
+ import { getRootDir } from '../mops.js';
7
+ import { createActor, idlFactory } from '../declarations/bench/index.js';
8
+ export class BenchReplica {
9
+ constructor(type, verbose = false) {
10
+ this.verbose = false;
11
+ this.canisters = {};
12
+ this.type = type;
13
+ this.verbose = verbose;
14
+ }
15
+ async start() {
16
+ console.log(`Starting ${this.type} replica...`);
17
+ if (this.type == 'dfx') {
18
+ await this.stop();
19
+ let dir = path.join(getRootDir(), '.mops/.bench');
20
+ fs.writeFileSync(path.join(dir, 'dfx.json'), JSON.stringify(this.dfxJson(''), null, 2));
21
+ execSync('dfx start --background --clean --artificial-delay 0' + (this.verbose ? '' : ' -qqqq'), { cwd: dir, stdio: ['inherit', this.verbose ? 'inherit' : 'ignore', 'inherit'] });
22
+ }
23
+ else {
24
+ this.pocketIc = await PocketIc.create();
25
+ }
26
+ }
27
+ async stop() {
28
+ if (this.type == 'dfx') {
29
+ let dir = path.join(getRootDir(), '.mops/.bench');
30
+ execSync('dfx stop' + (this.verbose ? '' : ' -qqqq'), { cwd: dir, stdio: ['pipe', this.verbose ? 'inherit' : 'ignore', 'pipe'] });
31
+ }
32
+ else if (this.pocketIc) {
33
+ this.pocketIc.tearDown();
34
+ }
35
+ }
36
+ async deploy(name, wasm, cwd = process.cwd()) {
37
+ if (this.type === 'dfx') {
38
+ await execaCommand(`dfx deploy ${name} --mode reinstall --yes --identity anonymous`, { cwd, stdio: this.verbose ? 'pipe' : ['pipe', 'ignore', 'pipe'] });
39
+ let canisterId = execSync(`dfx canister id ${name}`, { cwd }).toString().trim();
40
+ let actor = await createActor(canisterId, {
41
+ agentOptions: {
42
+ host: 'http://127.0.0.1:4944',
43
+ },
44
+ });
45
+ this.canisters[name] = { cwd, canisterId, actor };
46
+ }
47
+ else if (this.pocketIc) {
48
+ let { canisterId, actor } = await this.pocketIc.setupCanister(idlFactory, wasm);
49
+ this.canisters[name] = {
50
+ cwd,
51
+ canisterId: canisterId.toText(),
52
+ actor,
53
+ };
54
+ }
55
+ }
56
+ getActor(name) {
57
+ return this.canisters[name]?.actor;
58
+ }
59
+ getCanisterId(name) {
60
+ return this.canisters[name]?.canisterId || '';
61
+ }
62
+ dfxJson(canisterName) {
63
+ let canisters = {};
64
+ if (canisterName) {
65
+ canisters[canisterName] = {
66
+ type: 'custom',
67
+ wasm: 'canister.wasm',
68
+ candid: 'canister.did',
69
+ };
70
+ }
71
+ return {
72
+ version: 1,
73
+ canisters,
74
+ defaults: {
75
+ build: {
76
+ packtool: 'mops sources',
77
+ },
78
+ },
79
+ networks: {
80
+ local: {
81
+ type: 'ephemeral',
82
+ bind: '127.0.0.1:4944',
83
+ },
84
+ },
85
+ };
86
+ }
87
+ }
@@ -1,11 +1,12 @@
1
1
  type BenchOptions = {
2
- dfx?: string;
3
- moc?: string;
4
- gc?: 'copying' | 'compacting' | 'generational' | 'incremental';
5
- forceGc?: boolean;
6
- save?: boolean;
7
- compare?: boolean;
8
- verbose?: boolean;
2
+ replica: 'dfx' | 'pocket-ic';
3
+ replicaVersion: string;
4
+ moc: string;
5
+ gc: 'copying' | 'compacting' | 'generational' | 'incremental';
6
+ forceGc: boolean;
7
+ save: boolean;
8
+ compare: boolean;
9
+ verbose: boolean;
9
10
  };
10
- export declare function bench(filter?: string, options?: BenchOptions): Promise<boolean>;
11
+ export declare function bench(filter?: string, optionsArg?: Partial<BenchOptions>): Promise<boolean>;
11
12
  export {};
@@ -1,4 +1,3 @@
1
- import { execSync } from 'node:child_process';
2
1
  import path from 'node:path';
3
2
  import fs from 'node:fs';
4
3
  import os from 'node:os';
@@ -6,15 +5,16 @@ import chalk from 'chalk';
6
5
  import { globSync } from 'glob';
7
6
  import { markdownTable } from 'markdown-table';
8
7
  import logUpdate from 'log-update';
8
+ import { execaCommand } from 'execa';
9
+ import stringWidth from 'string-width';
9
10
  import { getRootDir } from '../mops.js';
10
11
  import { parallel } from '../parallel.js';
11
- import { createActor } from '../declarations/bench/index.js';
12
12
  import { absToRel } from './test/utils.js';
13
13
  import { getMocVersion } from '../helpers/get-moc-version.js';
14
14
  import { getDfxVersion } from '../helpers/get-dfx-version.js';
15
15
  import { getMocPath } from '../helpers/get-moc-path.js';
16
16
  import { sources } from './sources.js';
17
- import { execaCommand } from 'execa';
17
+ import { BenchReplica } from './bench-replica.js';
18
18
  let ignore = [
19
19
  '**/node_modules/**',
20
20
  '**/.mops/**',
@@ -25,18 +25,21 @@ let globConfig = {
25
25
  nocase: true,
26
26
  ignore: ignore,
27
27
  };
28
- export async function bench(filter = '', options = {}) {
28
+ export async function bench(filter = '', optionsArg = {}) {
29
29
  let defaultOptions = {
30
+ replica: 'dfx',
30
31
  moc: getMocVersion(),
31
- dfx: getDfxVersion(),
32
- gc: 'incremental',
32
+ replicaVersion: '0.0.0',
33
+ gc: 'copying',
33
34
  forceGc: true,
34
35
  save: false,
35
36
  compare: false,
36
37
  verbose: false,
37
38
  };
38
- options = { ...defaultOptions, ...options };
39
+ let options = { ...defaultOptions, ...optionsArg };
40
+ options.replicaVersion = options.replica == 'dfx' ? getDfxVersion() : '1.0.0';
39
41
  options.verbose && console.log(options);
42
+ let replica = new BenchReplica(options.replica, options.verbose);
40
43
  let rootDir = getRootDir();
41
44
  let globStr = '**/bench?(mark)/**/*.bench.mo';
42
45
  if (filter) {
@@ -63,16 +66,15 @@ export async function bench(filter = '', options = {}) {
63
66
  console.log('');
64
67
  console.log('='.repeat(50));
65
68
  console.log('');
66
- console.log('Starting dfx replica...');
67
- startDfx(options.verbose);
69
+ await replica.start();
68
70
  console.log('Deploying canisters...');
69
71
  await parallel(os.cpus().length, files, async (file) => {
70
72
  try {
71
- await deployBenchFile(file, options);
73
+ await deployBenchFile(file, options, replica);
72
74
  }
73
75
  catch (err) {
74
- console.error('Unexpected error. Stopping dfx replica...');
75
- stopDfx(options.verbose);
76
+ console.error('Unexpected error. Stopping replica...');
77
+ await replica.stop();
76
78
  throw err;
77
79
  }
78
80
  });
@@ -81,16 +83,16 @@ export async function bench(filter = '', options = {}) {
81
83
  console.log(`\nRunning ${chalk.gray(absToRel(file))}...`);
82
84
  console.log('');
83
85
  try {
84
- await runBenchFile(file, options);
86
+ await runBenchFile(file, options, replica);
85
87
  }
86
88
  catch (err) {
87
- console.error('Unexpected error. Stopping dfx replica...');
88
- stopDfx(options.verbose);
89
+ console.error('Unexpected error. Stopping replica...');
90
+ await replica.stop();
89
91
  throw err;
90
92
  }
91
93
  });
92
- console.log('Stopping dfx replica...');
93
- stopDfx(options.verbose);
94
+ console.log('Stopping replica...');
95
+ await replica.stop();
94
96
  fs.rmSync(benchDir, { recursive: true, force: true });
95
97
  return true;
96
98
  }
@@ -104,49 +106,13 @@ function getMocArgs(options) {
104
106
  }
105
107
  return args;
106
108
  }
107
- function dfxJson(canisterName, options = {}) {
108
- options || console.log(options);
109
- let canisters = {};
110
- if (canisterName) {
111
- canisters[canisterName] = {
112
- type: 'custom',
113
- wasm: 'canister.wasm',
114
- candid: 'canister.did',
115
- };
116
- }
117
- return {
118
- version: 1,
119
- canisters,
120
- defaults: {
121
- build: {
122
- packtool: 'mops sources',
123
- },
124
- },
125
- networks: {
126
- local: {
127
- type: 'ephemeral',
128
- bind: '127.0.0.1:4944',
129
- },
130
- },
131
- };
132
- }
133
- function startDfx(verbose = false) {
134
- stopDfx(verbose);
135
- let dir = path.join(getRootDir(), '.mops/.bench');
136
- fs.writeFileSync(path.join(dir, 'dfx.json'), JSON.stringify(dfxJson(''), null, 2));
137
- execSync('dfx start --background --clean --artificial-delay 0' + (verbose ? '' : ' -qqqq'), { cwd: dir, stdio: ['inherit', verbose ? 'inherit' : 'ignore', 'inherit'] });
138
- }
139
- function stopDfx(verbose = false) {
140
- let dir = path.join(getRootDir(), '.mops/.bench');
141
- execSync('dfx stop' + (verbose ? '' : ' -qqqq'), { cwd: dir, stdio: ['pipe', verbose ? 'inherit' : 'ignore', 'pipe'] });
142
- }
143
- async function deployBenchFile(file, options = {}) {
109
+ async function deployBenchFile(file, options, replica) {
144
110
  let rootDir = getRootDir();
145
111
  let tempDir = path.join(rootDir, '.mops/.bench/', path.parse(file).name);
146
112
  let canisterName = path.parse(file).name;
147
113
  // prepare temp files
148
114
  fs.mkdirSync(tempDir, { recursive: true });
149
- fs.writeFileSync(path.join(tempDir, 'dfx.json'), JSON.stringify(dfxJson(canisterName, options), null, 2));
115
+ fs.writeFileSync(path.join(tempDir, 'dfx.json'), JSON.stringify(replica.dfxJson(canisterName), null, 2));
150
116
  let benchCanisterData = fs.readFileSync(new URL('./bench/bench-canister.mo', import.meta.url), 'utf8');
151
117
  benchCanisterData = benchCanisterData.replace('./user-bench', path.relative(tempDir, file).replace(/.mo$/g, ''));
152
118
  fs.writeFileSync(path.join(tempDir, 'canister.mo'), benchCanisterData);
@@ -157,30 +123,21 @@ async function deployBenchFile(file, options = {}) {
157
123
  await execaCommand(`${mocPath} -c --idl canister.mo ${mocArgs} ${(await sources({ cwd: tempDir })).join(' ')}`, { cwd: tempDir, stdio: options.verbose ? 'pipe' : ['pipe', 'ignore', 'pipe'] });
158
124
  options.verbose && console.timeEnd(`build ${canisterName}`);
159
125
  // deploy canister
126
+ let wasm = path.join(tempDir, 'canister.wasm');
160
127
  options.verbose && console.time(`deploy ${canisterName}`);
161
- await execaCommand(`dfx deploy ${canisterName} --mode reinstall --yes --identity anonymous`, { cwd: tempDir, stdio: options.verbose ? 'pipe' : ['pipe', 'ignore', 'pipe'] });
128
+ // await execaCommand(`dfx deploy ${canisterName} --mode reinstall --yes --identity anonymous`, {cwd: tempDir, stdio: options.verbose ? 'pipe' : ['pipe', 'ignore', 'pipe']});
129
+ await replica.deploy(canisterName, wasm, tempDir);
162
130
  options.verbose && console.timeEnd(`deploy ${canisterName}`);
163
131
  // init bench
164
132
  options.verbose && console.time(`init ${canisterName}`);
165
- let canisterId = execSync(`dfx canister id ${canisterName}`, { cwd: tempDir }).toString().trim();
166
- let actor = await createActor(canisterId, {
167
- agentOptions: {
168
- host: 'http://127.0.0.1:4944',
169
- },
170
- });
133
+ let actor = await replica.getActor(canisterName);
171
134
  await actor.init();
172
135
  options.verbose && console.timeEnd(`init ${canisterName}`);
173
136
  }
174
- async function runBenchFile(file, options = {}) {
137
+ async function runBenchFile(file, options, replica) {
175
138
  let rootDir = getRootDir();
176
- let tempDir = path.join(rootDir, '.mops/.bench/', path.parse(file).name);
177
139
  let canisterName = path.parse(file).name;
178
- let canisterId = execSync(`dfx canister id ${canisterName}`, { cwd: tempDir }).toString().trim();
179
- let actor = await createActor(canisterId, {
180
- agentOptions: {
181
- host: 'http://127.0.0.1:4944',
182
- },
183
- });
140
+ let actor = await replica.getActor(canisterName);
184
141
  let schema = await actor.getSchema();
185
142
  // load previous results
186
143
  let prevResults;
@@ -213,8 +170,8 @@ async function runBenchFile(file, options = {}) {
213
170
  let percent = (Number(res[prop]) - Number(prevRes[prop])) / Number(prevRes[prop]) * 100;
214
171
  let sign = percent > 0 ? '+' : '';
215
172
  let percentText = percent == 0 ? '0%' : sign + percent.toFixed(2) + '%';
216
- // diff = ' (' + (percent > 0 ? chalk.red(percentText) : chalk.green(percentText)) + ')'; // alignment is broken
217
- diff = ' (' + percentText + ')';
173
+ let color = percent == 0 ? 'gray' : (percent > 0 ? 'red' : 'green');
174
+ diff = ` (${chalk[color](percentText)})`;
218
175
  }
219
176
  else {
220
177
  diff = chalk.yellow(' (no previous results)');
@@ -229,7 +186,10 @@ async function runBenchFile(file, options = {}) {
229
186
  }
230
187
  resArr.push(curRow);
231
188
  }
232
- return markdownTable(resArr, { align: ['l', ...'r'.repeat(schema.cols.length)] });
189
+ return markdownTable(resArr, {
190
+ align: ['l', ...'r'.repeat(schema.cols.length)],
191
+ stringLength: stringWidth,
192
+ });
233
193
  };
234
194
  let printResults = () => {
235
195
  logUpdate(`
@@ -239,7 +199,9 @@ async function runBenchFile(file, options = {}) {
239
199
  \n\n${chalk.blue('Heap')}\n\n${getTable('rts_heap_size')}
240
200
  `);
241
201
  };
242
- printResults();
202
+ if (!process.env.CI) {
203
+ printResults();
204
+ }
243
205
  // run all cells
244
206
  for (let [rowIndex, row] of schema.rows.entries()) {
245
207
  for (let [colIndex, col] of schema.cols.entries()) {
@@ -247,9 +209,14 @@ async function runBenchFile(file, options = {}) {
247
209
  // let res = await actor.runCellUpdate(BigInt(rowIndex), BigInt(colIndex));
248
210
  let res = await actor.runCellUpdateAwait(BigInt(rowIndex), BigInt(colIndex));
249
211
  results.set(`${row}:${col}`, res);
250
- printResults();
212
+ if (!process.env.CI) {
213
+ printResults();
214
+ }
251
215
  }
252
216
  }
217
+ if (process.env.CI) {
218
+ printResults();
219
+ }
253
220
  logUpdate.done();
254
221
  // save results
255
222
  if (options.save) {
@@ -257,7 +224,8 @@ async function runBenchFile(file, options = {}) {
257
224
  let json = {
258
225
  version: 1,
259
226
  moc: options.moc,
260
- dfx: options.dfx,
227
+ replica: options.replica,
228
+ replicaVersion: options.replicaVersion,
261
229
  gc: options.gc,
262
230
  forceGc: options.forceGc,
263
231
  results: Array.from(results.entries()),
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "0.35.0",
3
+ "version": "0.36.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/cli.js"
@@ -19,7 +19,7 @@
19
19
  "author": "Zen Voich <zen.voich@gmail.com>",
20
20
  "license": "MIT",
21
21
  "engines": {
22
- "node": ">=16.0.0"
22
+ "node": ">=18.0.0"
23
23
  },
24
24
  "scripts": {
25
25
  "build": "tsc",
@@ -32,11 +32,12 @@
32
32
  "esbuild": "esbuild"
33
33
  },
34
34
  "dependencies": {
35
- "@dfinity/agent": "^0.18.1",
36
- "@dfinity/candid": "^0.18.1",
37
- "@dfinity/identity": "^0.18.1",
38
- "@dfinity/identity-secp256k1": "^0.18.1",
39
- "@dfinity/principal": "^0.18.1",
35
+ "@dfinity/agent": "^0.19.3",
36
+ "@dfinity/candid": "^0.19.3",
37
+ "@dfinity/identity": "^0.19.3",
38
+ "@dfinity/identity-secp256k1": "^0.19.3",
39
+ "@dfinity/principal": "^0.19.3",
40
+ "@hadronous/pic": "0.2.0",
40
41
  "@iarna/toml": "^2.2.5",
41
42
  "@noble/hashes": "1.3.2",
42
43
  "as-table": "^1.0.55",
@@ -55,7 +56,7 @@
55
56
  "glob": "^10.3.3",
56
57
  "globby": "^13.2.2",
57
58
  "got": "13.0.0",
58
- "log-update": "^5.0.1",
59
+ "log-update": "6.0.0",
59
60
  "markdown-table": "3.0.3",
60
61
  "mdast-util-from-markdown": "^2.0.0",
61
62
  "mdast-util-to-markdown": "^2.1.0",
@@ -65,6 +66,7 @@
65
66
  "pem-file": "^1.0.1",
66
67
  "prompts": "^2.4.2",
67
68
  "stream-to-promise": "^3.0.0",
69
+ "string-width": "7.0.0",
68
70
  "tar": "^6.1.15"
69
71
  },
70
72
  "devDependencies": {
@@ -78,7 +80,13 @@
78
80
  "@types/stream-to-promise": "^2.2.1",
79
81
  "@types/tar": "^6.1.5",
80
82
  "esbuild": "^0.18.16",
81
- "tsx": "^3.12.7",
83
+ "tsx": "4.6.2",
82
84
  "typescript": "^5.1.6"
85
+ },
86
+ "overrides": {
87
+ "@dfinity/agent": "^0.19.3",
88
+ "@dfinity/identity": "^0.19.3",
89
+ "@dfinity/principal": "^0.19.3",
90
+ "@dfinity/candid": "^0.19.3"
83
91
  }
84
92
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "0.35.0",
3
+ "version": "0.36.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/cli.js"
@@ -19,7 +19,7 @@
19
19
  "author": "Zen Voich <zen.voich@gmail.com>",
20
20
  "license": "MIT",
21
21
  "engines": {
22
- "node": ">=16.0.0"
22
+ "node": ">=18.0.0"
23
23
  },
24
24
  "scripts": {
25
25
  "build": "tsc",
@@ -32,11 +32,12 @@
32
32
  "esbuild": "esbuild"
33
33
  },
34
34
  "dependencies": {
35
- "@dfinity/agent": "^0.18.1",
36
- "@dfinity/candid": "^0.18.1",
37
- "@dfinity/identity": "^0.18.1",
38
- "@dfinity/identity-secp256k1": "^0.18.1",
39
- "@dfinity/principal": "^0.18.1",
35
+ "@dfinity/agent": "^0.19.3",
36
+ "@dfinity/candid": "^0.19.3",
37
+ "@dfinity/identity": "^0.19.3",
38
+ "@dfinity/identity-secp256k1": "^0.19.3",
39
+ "@dfinity/principal": "^0.19.3",
40
+ "@hadronous/pic": "0.2.0",
40
41
  "@iarna/toml": "^2.2.5",
41
42
  "@noble/hashes": "1.3.2",
42
43
  "as-table": "^1.0.55",
@@ -55,7 +56,7 @@
55
56
  "glob": "^10.3.3",
56
57
  "globby": "^13.2.2",
57
58
  "got": "13.0.0",
58
- "log-update": "^5.0.1",
59
+ "log-update": "6.0.0",
59
60
  "markdown-table": "3.0.3",
60
61
  "mdast-util-from-markdown": "^2.0.0",
61
62
  "mdast-util-to-markdown": "^2.1.0",
@@ -65,6 +66,7 @@
65
66
  "pem-file": "^1.0.1",
66
67
  "prompts": "^2.4.2",
67
68
  "stream-to-promise": "^3.0.0",
69
+ "string-width": "7.0.0",
68
70
  "tar": "^6.1.15"
69
71
  },
70
72
  "devDependencies": {
@@ -78,7 +80,13 @@
78
80
  "@types/stream-to-promise": "^2.2.1",
79
81
  "@types/tar": "^6.1.5",
80
82
  "esbuild": "^0.18.16",
81
- "tsx": "^3.12.7",
83
+ "tsx": "4.6.2",
82
84
  "typescript": "^5.1.6"
85
+ },
86
+ "overrides": {
87
+ "@dfinity/agent": "^0.19.3",
88
+ "@dfinity/identity": "^0.19.3",
89
+ "@dfinity/principal": "^0.19.3",
90
+ "@dfinity/candid": "^0.19.3"
83
91
  }
84
92
  }