@libp2p/perf 1.1.14 → 1.1.15-78db573f9

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.
@@ -0,0 +1,169 @@
1
+ import { logger } from '@libp2p/logger';
2
+ import { pushable } from 'it-pushable';
3
+ import { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, PROTOCOL_NAME, RUN_ON_TRANSIENT_CONNECTION, WRITE_BLOCK_SIZE } from './constants.js';
4
+ const log = logger('libp2p:perf');
5
+ export class PerfService {
6
+ protocol;
7
+ components;
8
+ started;
9
+ databuf;
10
+ writeBlockSize;
11
+ maxInboundStreams;
12
+ maxOutboundStreams;
13
+ runOnTransientConnection;
14
+ constructor(components, init = {}) {
15
+ this.components = components;
16
+ this.started = false;
17
+ this.protocol = init.protocolName ?? PROTOCOL_NAME;
18
+ this.writeBlockSize = init.writeBlockSize ?? WRITE_BLOCK_SIZE;
19
+ this.databuf = new ArrayBuffer(this.writeBlockSize);
20
+ this.maxInboundStreams = init.maxInboundStreams ?? MAX_INBOUND_STREAMS;
21
+ this.maxOutboundStreams = init.maxOutboundStreams ?? MAX_OUTBOUND_STREAMS;
22
+ this.runOnTransientConnection = init.runOnTransientConnection ?? RUN_ON_TRANSIENT_CONNECTION;
23
+ }
24
+ async start() {
25
+ await this.components.registrar.handle(this.protocol, (data) => {
26
+ void this.handleMessage(data).catch((err) => {
27
+ log.error('error handling perf protocol message', err);
28
+ });
29
+ }, {
30
+ maxInboundStreams: this.maxInboundStreams,
31
+ maxOutboundStreams: this.maxOutboundStreams,
32
+ runOnTransientConnection: this.runOnTransientConnection
33
+ });
34
+ this.started = true;
35
+ }
36
+ async stop() {
37
+ await this.components.registrar.unhandle(this.protocol);
38
+ this.started = false;
39
+ }
40
+ isStarted() {
41
+ return this.started;
42
+ }
43
+ async handleMessage(data) {
44
+ const { stream } = data;
45
+ try {
46
+ const writeBlockSize = this.writeBlockSize;
47
+ let bytesToSendBack;
48
+ for await (const buf of stream.source) {
49
+ if (bytesToSendBack == null) {
50
+ // downcast 64 to 52 bits to avoid bigint arithmetic performance penalty
51
+ bytesToSendBack = Number(buf.getBigUint64(0, false));
52
+ }
53
+ // Ingest all the bufs and wait for the read side to close
54
+ }
55
+ if (bytesToSendBack == null) {
56
+ throw new Error('bytesToSendBack was not set');
57
+ }
58
+ const uint8Buf = new Uint8Array(this.databuf);
59
+ await stream.sink(async function* () {
60
+ while (bytesToSendBack > 0) {
61
+ let toSend = writeBlockSize;
62
+ if (toSend > bytesToSendBack) {
63
+ toSend = bytesToSendBack;
64
+ }
65
+ bytesToSendBack = bytesToSendBack - toSend;
66
+ yield uint8Buf.subarray(0, toSend);
67
+ }
68
+ }());
69
+ }
70
+ catch (err) {
71
+ stream.abort(err);
72
+ }
73
+ }
74
+ async *measurePerformance(ma, sendBytes, receiveBytes, options = {}) {
75
+ log('opening stream on protocol %s to %a', this.protocol, ma);
76
+ const uint8Buf = new Uint8Array(this.databuf);
77
+ const writeBlockSize = this.writeBlockSize;
78
+ // start time should include connection establishment
79
+ const initialStartTime = Date.now();
80
+ const connection = await this.components.connectionManager.openConnection(ma, {
81
+ ...options,
82
+ force: options.reuseExistingConnection !== true
83
+ });
84
+ const stream = await connection.newStream(this.protocol, options);
85
+ let lastAmountOfBytesSent = 0;
86
+ let lastReportedTime = Date.now();
87
+ let totalBytesSent = 0;
88
+ // tell the remote how many bytes we will send. Up cast to 64 bit number
89
+ // as if we send as ui32 we limit total transfer size to 4GB
90
+ const view = new DataView(this.databuf);
91
+ view.setBigUint64(0, BigInt(receiveBytes), false);
92
+ log('sending %i bytes to %p', sendBytes, connection.remotePeer);
93
+ try {
94
+ const sendOutput = pushable({
95
+ objectMode: true
96
+ });
97
+ void stream.sink(async function* () {
98
+ // Send the number of bytes to receive
99
+ yield uint8Buf.subarray(0, 8);
100
+ while (sendBytes > 0) {
101
+ options.signal?.throwIfAborted();
102
+ let toSend = writeBlockSize;
103
+ if (toSend > sendBytes) {
104
+ toSend = sendBytes;
105
+ }
106
+ sendBytes = sendBytes - toSend;
107
+ yield uint8Buf.subarray(0, toSend);
108
+ if (Date.now() - lastReportedTime > 1000) {
109
+ sendOutput.push({
110
+ type: 'intermediary',
111
+ timeSeconds: (Date.now() - lastReportedTime) / 1000,
112
+ uploadBytes: lastAmountOfBytesSent,
113
+ downloadBytes: 0
114
+ });
115
+ // record last reported time after `console.log` because it can
116
+ // affect benchmark timings
117
+ lastReportedTime = Date.now();
118
+ lastAmountOfBytesSent = 0;
119
+ }
120
+ lastAmountOfBytesSent += toSend;
121
+ totalBytesSent += toSend;
122
+ }
123
+ sendOutput.end();
124
+ }())
125
+ .catch(err => {
126
+ sendOutput.end(err);
127
+ });
128
+ yield* sendOutput;
129
+ // Read the received bytes
130
+ let lastAmountOfBytesReceived = 0;
131
+ lastReportedTime = Date.now();
132
+ let totalBytesReceived = 0;
133
+ for await (const buf of stream.source) {
134
+ options.signal?.throwIfAborted();
135
+ if (Date.now() - lastReportedTime > 1000) {
136
+ yield {
137
+ type: 'intermediary',
138
+ timeSeconds: (Date.now() - lastReportedTime) / 1000,
139
+ uploadBytes: 0,
140
+ downloadBytes: lastAmountOfBytesReceived
141
+ };
142
+ // record last reported time after `console.log` because it can
143
+ // affect benchmark timings
144
+ lastReportedTime = Date.now();
145
+ lastAmountOfBytesReceived = 0;
146
+ }
147
+ lastAmountOfBytesReceived += buf.byteLength;
148
+ totalBytesReceived += buf.byteLength;
149
+ }
150
+ if (totalBytesReceived !== receiveBytes) {
151
+ throw new Error(`Expected to receive ${receiveBytes} bytes, but received ${totalBytesReceived}`);
152
+ }
153
+ yield {
154
+ type: 'final',
155
+ timeSeconds: (Date.now() - initialStartTime) / 1000,
156
+ uploadBytes: totalBytesSent,
157
+ downloadBytes: totalBytesReceived
158
+ };
159
+ log('performed %s to %p', this.protocol, connection.remotePeer);
160
+ await stream.close();
161
+ }
162
+ catch (err) {
163
+ log('error sending %s bytes to %p: %s', totalBytesSent, connection.remotePeer, err);
164
+ stream.abort(err);
165
+ throw err;
166
+ }
167
+ }
168
+ }
169
+ //# sourceMappingURL=perf-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf-service.js","sourceRoot":"","sources":["../../src/perf-service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libp2p/perf",
3
- "version": "1.1.14",
3
+ "version": "1.1.15-78db573f9",
4
4
  "description": "Implementation of Perf Protocol",
5
5
  "author": "@maschad / @marcopolo",
6
6
  "license": "Apache-2.0 OR MIT",
@@ -48,22 +48,17 @@
48
48
  "renderResults": "node dist/src/renderResults.js"
49
49
  },
50
50
  "dependencies": {
51
- "@chainsafe/libp2p-noise": "^13.0.0",
52
- "@chainsafe/libp2p-yamux": "^5.0.0",
53
- "@libp2p/crypto": "^2.0.7",
54
- "@libp2p/interface": "^0.1.5",
55
- "@libp2p/interface-compliance-tests": "^4.1.3",
56
- "@libp2p/interface-internal": "^0.1.8",
57
- "@libp2p/interfaces": "3.3.2",
58
- "@libp2p/logger": "^3.0.5",
59
- "@libp2p/peer-id-factory": "^3.0.7",
60
- "@libp2p/tcp": "^8.0.11",
61
- "@multiformats/multiaddr": "^12.1.5",
62
- "libp2p": "^0.46.17",
63
- "uint8arrays": "^4.0.6",
64
- "yargs": "^17.7.2"
51
+ "@libp2p/interface": "0.1.5-78db573f9",
52
+ "@libp2p/interface-internal": "0.1.8-78db573f9",
53
+ "@libp2p/logger": "3.0.5-78db573f9",
54
+ "@multiformats/multiaddr": "^12.1.10",
55
+ "it-pushable": "^3.2.1"
65
56
  },
66
57
  "devDependencies": {
67
- "aegir": "^41.0.2"
58
+ "@libp2p/interface-compliance-tests": "4.1.3-78db573f9",
59
+ "aegir": "^41.0.2",
60
+ "it-last": "^3.0.3",
61
+ "it-pair": "^2.0.6",
62
+ "sinon-ts": "^2.0.0"
68
63
  }
69
64
  }
package/src/constants.ts CHANGED
@@ -1,2 +1,5 @@
1
1
  export const PROTOCOL_NAME = '/perf/1.0.0'
2
- export const WRITE_BLOCK_SIZE = BigInt(64 << 10)
2
+ export const WRITE_BLOCK_SIZE = 64 << 10
3
+ export const MAX_INBOUND_STREAMS = 1
4
+ export const MAX_OUTBOUND_STREAMS = 1
5
+ export const RUN_ON_TRANSIENT_CONNECTION = false
package/src/index.ts CHANGED
@@ -1,71 +1,95 @@
1
1
  /**
2
2
  * @packageDocumentation
3
3
  *
4
- * The `performanceService` implements the [perf protocol](https://github.com/libp2p/specs/blob/master/perf/perf.md), which is used to measure performance within and across libp2p implementations
5
- * addresses.
4
+ * The {@link PerfService} implements the [perf protocol](https://github.com/libp2p/specs/blob/master/perf/perf.md), which can be used to measure transfer performance within and across libp2p implementations.
6
5
  *
7
6
  * @example
8
7
  *
9
8
  * ```typescript
10
- * import { createLibp2p } from 'libp2p'
11
- * import { perfService } from '@libp2p/perf'
9
+ * import { noise } from '@chainsafe/libp2p-noise'
10
+ * import { yamux } from '@chainsafe/libp2p-yamux'
11
+ * import { mplex } from '@libp2p/mplex'
12
+ * import { tcp } from '@libp2p/tcp'
13
+ * import { createLibp2p, type Libp2p } from 'libp2p'
14
+ * import { plaintext } from 'libp2p/insecure'
15
+ * import { perfService, type PerfService } from '@libp2p/perf'
12
16
  *
13
- * const node = await createLibp2p({
14
- * service: [
15
- * perfService()
16
- * ]
17
- * })
18
- * ```
19
- *
20
- * The `measurePerformance` function can be used to measure the latency and throughput of a connection.
21
- * server. This will not work in browsers.
17
+ * const ONE_MEG = 1024 * 1024
18
+ * const UPLOAD_BYTES = ONE_MEG * 1024
19
+ * const DOWNLOAD_BYTES = ONE_MEG * 1024
22
20
  *
23
- * @example
24
- *
25
- * ```typescript
26
- * import { createLibp2p } from 'libp2p'
27
- * import { perfService } from 'libp2p/perf'
21
+ * async function createNode (): Promise<Libp2p<{ perf: PerfService }>> {
22
+ * return createLibp2p({
23
+ * addresses: {
24
+ * listen: [
25
+ * '/ip4/0.0.0.0/tcp/0'
26
+ * ]
27
+ * },
28
+ * transports: [
29
+ * tcp()
30
+ * ],
31
+ * connectionEncryption: [
32
+ * noise(), plaintext()
33
+ * ],
34
+ * streamMuxers: [
35
+ * yamux(), mplex()
36
+ * ],
37
+ * services: {
38
+ * perf: perfService()
39
+ * }
40
+ * })
41
+ * }
28
42
  *
29
- * const node = await createLibp2p({
30
- * services: [
31
- * perf: perfService()
32
- * ]
33
- * })
43
+ * const libp2p1 = await createNode()
44
+ * const libp2p2 = await createNode()
34
45
  *
35
- * const connection = await node.dial(multiaddr(multiaddrAddress))
36
- *
37
- * const startTime = Date.now()
38
- *
39
- * await node.services.perf.measurePerformance(startTime, connection, BigInt(uploadBytes), BigInt(downloadBytes))
46
+ * for await (const output of libp2p1.services.perf.measurePerformance(libp2p2.getMultiaddrs()[0], UPLOAD_BYTES, DOWNLOAD_BYTES)) {
47
+ * console.info(output)
48
+ * }
40
49
  *
50
+ * await libp2p1.stop()
51
+ * await libp2p2.stop()
41
52
  * ```
42
53
  */
43
54
 
44
- import { logger } from '@libp2p/logger'
45
- import { PROTOCOL_NAME, WRITE_BLOCK_SIZE } from './constants.js'
46
- import type { Connection } from '@libp2p/interface/connection'
47
- import type { Startable } from '@libp2p/interface/startable'
55
+ import { PerfService as PerfServiceClass } from './perf-service.js'
56
+ import type { AbortOptions } from '@libp2p/interface'
48
57
  import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager'
49
- import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar'
50
- import type { AbortOptions } from '@libp2p/interfaces'
51
-
52
- const log = logger('libp2p:perf')
53
-
54
- export const defaultInit: PerfServiceInit = {
55
- protocolName: '/perf/1.0.0',
56
- writeBlockSize: BigInt(64 << 10)
58
+ import type { Registrar } from '@libp2p/interface-internal/registrar'
59
+ import type { Multiaddr } from '@multiformats/multiaddr'
60
+
61
+ export interface PerfOptions extends AbortOptions {
62
+ /**
63
+ * By default measuring perf should include the time it takes to establish a
64
+ * connection, so a new connection will be opened for every performance run.
65
+ *
66
+ * To override this and re-use an existing connection if one is present, pass
67
+ * `true` here. (default: false)
68
+ */
69
+ reuseExistingConnection?: boolean
57
70
  }
58
71
 
59
72
  export interface PerfService {
60
- measurePerformance(startTime: number, connection: Connection, sendBytes: bigint, recvBytes: bigint, options?: AbortOptions): Promise<number>
73
+ measurePerformance(multiaddr: Multiaddr, sendBytes: number, recvBytes: number, options?: PerfOptions): AsyncGenerator<PerfOutput>
74
+ }
75
+
76
+ export interface PerfOutput {
77
+ type: 'intermediary' | 'final'
78
+ timeSeconds: number
79
+ uploadBytes: number
80
+ downloadBytes: number
61
81
  }
62
82
 
63
83
  export interface PerfServiceInit {
64
84
  protocolName?: string
65
85
  maxInboundStreams?: number
66
86
  maxOutboundStreams?: number
67
- timeout?: number
68
- writeBlockSize?: bigint
87
+ runOnTransientConnection?: boolean
88
+
89
+ /**
90
+ * Data sent/received will be sent in chunks of this size (default: 64KiB)
91
+ */
92
+ writeBlockSize?: number
69
93
  }
70
94
 
71
95
  export interface PerfServiceComponents {
@@ -73,122 +97,6 @@ export interface PerfServiceComponents {
73
97
  connectionManager: ConnectionManager
74
98
  }
75
99
 
76
- class DefaultPerfService implements Startable, PerfService {
77
- public readonly protocol: string
78
- private readonly components: PerfServiceComponents
79
- private started: boolean
80
- private readonly databuf: ArrayBuffer
81
- private readonly writeBlockSize: bigint
82
-
83
- constructor (components: PerfServiceComponents, init: PerfServiceInit) {
84
- this.components = components
85
- this.started = false
86
- this.protocol = init.protocolName ?? PROTOCOL_NAME
87
- this.writeBlockSize = init.writeBlockSize ?? WRITE_BLOCK_SIZE
88
- this.databuf = new ArrayBuffer(Number(init.writeBlockSize))
89
- }
90
-
91
- async start (): Promise<void> {
92
- await this.components.registrar.handle(this.protocol, (data: IncomingStreamData) => {
93
- void this.handleMessage(data).catch((err) => {
94
- log.error('error handling perf protocol message', err)
95
- })
96
- })
97
- this.started = true
98
- }
99
-
100
- async stop (): Promise<void> {
101
- await this.components.registrar.unhandle(this.protocol)
102
- this.started = false
103
- }
104
-
105
- isStarted (): boolean {
106
- return this.started
107
- }
108
-
109
- async handleMessage (data: IncomingStreamData): Promise<void> {
110
- const { stream } = data
111
-
112
- const writeBlockSize = this.writeBlockSize
113
-
114
- let bytesToSendBack: bigint | null = null
115
-
116
- for await (const buf of stream.source) {
117
- if (bytesToSendBack === null) {
118
- bytesToSendBack = BigInt(buf.getBigUint64(0, false))
119
- }
120
- // Ingest all the bufs and wait for the read side to close
121
- }
122
-
123
- const uint8Buf = new Uint8Array(this.databuf)
124
-
125
- if (bytesToSendBack === null) {
126
- throw new Error('bytesToSendBack was not set')
127
- }
128
-
129
- await stream.sink(async function * () {
130
- while (bytesToSendBack > 0n) {
131
- let toSend: bigint = writeBlockSize
132
- if (toSend > bytesToSendBack) {
133
- toSend = bytesToSendBack
134
- }
135
- bytesToSendBack = bytesToSendBack - toSend
136
- yield uint8Buf.subarray(0, Number(toSend))
137
- }
138
- }())
139
- }
140
-
141
- async measurePerformance (startTime: number, connection: Connection, sendBytes: bigint, recvBytes: bigint, options: AbortOptions = {}): Promise<number> {
142
- log('opening stream on protocol %s to %p', this.protocol, connection.remotePeer)
143
-
144
- const uint8Buf = new Uint8Array(this.databuf)
145
-
146
- const writeBlockSize = this.writeBlockSize
147
-
148
- const stream = await connection.newStream([this.protocol], options)
149
-
150
- // Convert sendBytes to uint64 big endian buffer
151
- const view = new DataView(this.databuf)
152
- view.setBigInt64(0, recvBytes, false)
153
-
154
- log('sending %i bytes to %p', sendBytes, connection.remotePeer)
155
- try {
156
- await stream.sink((async function * () {
157
- // Send the number of bytes to receive
158
- yield uint8Buf.subarray(0, 8)
159
- // Send the number of bytes to send
160
- while (sendBytes > 0n) {
161
- let toSend: bigint = writeBlockSize
162
- if (toSend > sendBytes) {
163
- toSend = sendBytes
164
- }
165
- sendBytes = sendBytes - toSend
166
- yield uint8Buf.subarray(0, Number(toSend))
167
- }
168
- })())
169
-
170
- // Read the received bytes
171
- let actualRecvdBytes = BigInt(0)
172
- for await (const buf of stream.source) {
173
- actualRecvdBytes += BigInt(buf.length)
174
- }
175
-
176
- if (actualRecvdBytes !== recvBytes) {
177
- throw new Error(`Expected to receive ${recvBytes} bytes, but received ${actualRecvdBytes}`)
178
- }
179
- } catch (err) {
180
- log('error sending %i bytes to %p: %s', sendBytes, connection.remotePeer, err)
181
- throw err
182
- } finally {
183
- log('performed %s to %p', this.protocol, connection.remotePeer)
184
- await stream.close()
185
- }
186
-
187
- // Return the latency
188
- return Date.now() - startTime
189
- }
190
- }
191
-
192
100
  export function perfService (init: PerfServiceInit = {}): (components: PerfServiceComponents) => PerfService {
193
- return (components) => new DefaultPerfService(components, init)
101
+ return (components) => new PerfServiceClass(components, init)
194
102
  }