@typed/fx 0.0.32 → 0.0.34
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/.eslintcache +1 -1
- package/cjs/Subject.js +2 -2
- package/cjs/Subject.js.map +1 -1
- package/cjs/_internal.d.ts +1 -1
- package/cjs/_internal.d.ts.map +1 -1
- package/esm/Subject.js +2 -2
- package/esm/Subject.js.map +1 -1
- package/esm/_internal.d.ts +1 -1
- package/esm/_internal.d.ts.map +1 -1
- package/package.json +27 -31
- package/perf/cases/filter-map-reduce.ts +30 -38
- package/perf/helpers.ts +54 -0
- package/src/Subject.test.ts +1 -1
- package/src/Subject.ts +2 -2
- package/src/combine.test.ts +1 -1
- package/src/continueWith.test.ts +1 -1
- package/src/debounce.test.ts +1 -1
- package/src/delay.test.ts +1 -1
- package/src/during.test.ts +1 -1
- package/src/exhaustMap.test.ts +1 -1
- package/src/exhaustMapLatest.test.ts +1 -1
- package/src/exhaustMapList.test.ts +1 -1
- package/src/filterLoop.test.ts +1 -1
- package/src/filterMap.test.ts +1 -1
- package/src/flatMap.test.ts +1 -1
- package/src/flatMapConcurrently.test.ts +1 -1
- package/src/hold.test.ts +1 -1
- package/src/mapCause.test.ts +1 -1
- package/src/mapEffect.test.ts +1 -1
- package/src/merge.test.ts +1 -1
- package/src/multicast.test.ts +1 -1
- package/src/orElse.test.ts +1 -1
- package/src/periodic.test.ts +1 -1
- package/src/provide.test.ts +1 -1
- package/src/since.test.ts +1 -1
- package/src/skipRepeats.test.ts +1 -1
- package/src/skipWhile.test.ts +1 -1
- package/src/slice.test.ts +1 -1
- package/src/snapshot.test.ts +1 -1
- package/src/switchMap.test.ts +1 -1
- package/src/takeWhile.test.ts +1 -1
- package/src/throttle.test.ts +1 -1
- package/src/until.test.ts +1 -1
- package/src/withEmitter.test.ts +1 -1
- package/src/zip.test.ts +1 -1
- package/src/zipIterable.test.ts +1 -1
- package/tools/benchmark.ts +179 -0
- package/perf/_internal.ts +0 -153
- package/perf/cases/flatMap.ts +0 -49
- package/perf/cases/switchMap.ts +0 -36
- package/perf/readme-base.md +0 -14
- package/perf/readme.md +0 -40
- package/perf/runFullSuite.ts +0 -37
- package/perf/runPerfTest.ts +0 -18
- package/perf/tsconfig.json +0 -7
package/perf/_internal.ts
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import process from 'node:process'
|
|
2
|
-
|
|
3
|
-
import * as EffectStream from '@effect/core/Stream/Stream'
|
|
4
|
-
import * as Effect from '@effect/core/io/Effect'
|
|
5
|
-
import * as M from '@most/core'
|
|
6
|
-
import * as MS from '@most/scheduler'
|
|
7
|
-
import * as MT from '@most/types'
|
|
8
|
-
import benchmark, { Suite } from 'benchmark'
|
|
9
|
-
import * as RxJS from 'rxjs'
|
|
10
|
-
|
|
11
|
-
import * as Fx from '@/index.js'
|
|
12
|
-
|
|
13
|
-
export function parseIterations() {
|
|
14
|
-
const i = parseInt(process.argv[2], 10)
|
|
15
|
-
|
|
16
|
-
return Number.isNaN(i) ? 10000 : i
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const iterations = parseIterations()
|
|
20
|
-
export const array = Array.from({ length: iterations }, (_, i) => i)
|
|
21
|
-
|
|
22
|
-
export function runSuite(suite: Suite, cb: () => void) {
|
|
23
|
-
suite
|
|
24
|
-
.on('start', logStart)
|
|
25
|
-
.on('cycle', logResults)
|
|
26
|
-
.on('complete', () => {
|
|
27
|
-
logComplete()
|
|
28
|
-
cb()
|
|
29
|
-
})
|
|
30
|
-
.run({ defer: false })
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const longestLibrary = 13
|
|
34
|
-
const longestOps = 10
|
|
35
|
-
|
|
36
|
-
function logResults(e: any) {
|
|
37
|
-
const t = e.target
|
|
38
|
-
|
|
39
|
-
if (t.failure) {
|
|
40
|
-
console.log(`| ${t.name} | FAILED | ${e.target.failure} |`)
|
|
41
|
-
|
|
42
|
-
console.error(padl(10, t.name) + 'FAILED: ' + e.target.failure)
|
|
43
|
-
} else {
|
|
44
|
-
console.log(
|
|
45
|
-
`| ${padl(longestLibrary, t.name)} | ${padl(longestOps, t.hz.toFixed(2))} | ${padl(
|
|
46
|
-
6,
|
|
47
|
-
t.stats.rme.toFixed(2) + '%',
|
|
48
|
-
)} | ${padl(3, t.stats.sample.length)} |`,
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function logStart(this: any) {
|
|
54
|
-
console.log('### ' + this.name)
|
|
55
|
-
console.log(`| Library | Ops/sec | ± | Samples |
|
|
56
|
-
| --------------|------------|--------|---------|`)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function logComplete() {
|
|
60
|
-
console.log('-------------------------------------------------------\n')
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function padl(n: number, s: string) {
|
|
64
|
-
while (s.length < n) {
|
|
65
|
-
s += ' '
|
|
66
|
-
}
|
|
67
|
-
return s
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// function padr(n: number, s: string) {
|
|
71
|
-
// while (s.length < n) {
|
|
72
|
-
// s = ' ' + s
|
|
73
|
-
// }
|
|
74
|
-
// return s
|
|
75
|
-
// }
|
|
76
|
-
|
|
77
|
-
export interface PerformanceTest {
|
|
78
|
-
readonly name: string
|
|
79
|
-
readonly cases: readonly PerformanceTestCase<any>[]
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface PerformanceTestCase<A> {
|
|
83
|
-
name: string
|
|
84
|
-
init: () => A
|
|
85
|
-
run: (a: A, deferred: benchmark.Deferred) => void
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function PerformanceTestCase<A>(
|
|
89
|
-
name: string,
|
|
90
|
-
init: () => A,
|
|
91
|
-
run: (a: A, deferred: benchmark.Deferred) => void,
|
|
92
|
-
): PerformanceTestCase<A> {
|
|
93
|
-
return { name, init, run }
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function runPerformanceTest(test: PerformanceTest, cb: () => void) {
|
|
97
|
-
// eslint-disable-next-line import/no-named-as-default-member
|
|
98
|
-
let suite = new benchmark.Suite(test.name)
|
|
99
|
-
|
|
100
|
-
for (const testCase of test.cases) {
|
|
101
|
-
const constructed = testCase.init()
|
|
102
|
-
|
|
103
|
-
suite = suite.add(testCase.name, (deferred: benchmark.Deferred) =>
|
|
104
|
-
testCase.run(constructed, deferred),
|
|
105
|
-
)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
runSuite(suite, cb)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function fxTest<E, A>(init: () => Fx.Fx<never, E, A>) {
|
|
112
|
-
return PerformanceTestCase(
|
|
113
|
-
'Fx',
|
|
114
|
-
() => {
|
|
115
|
-
const push = init()
|
|
116
|
-
|
|
117
|
-
return Fx.runDrain(push)
|
|
118
|
-
},
|
|
119
|
-
(fx, deferred) => Effect.unsafeRunAsyncWith(fx, () => deferred.resolve()),
|
|
120
|
-
)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function fxEffectTest<E, A>(init: () => Effect.Effect<never, E, A>) {
|
|
124
|
-
return PerformanceTestCase('Fx', init, (e, deferred) => {
|
|
125
|
-
Effect.unsafeRunPromise(e).then(() => deferred.resolve())
|
|
126
|
-
})
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export function mostStreamTest<A>(init: () => MT.Stream<A>) {
|
|
130
|
-
const mostScheduler = MS.newDefaultScheduler()
|
|
131
|
-
|
|
132
|
-
return PerformanceTestCase('@most/core', init, (mostStream, deferred) =>
|
|
133
|
-
M.runEffects(mostStream, mostScheduler).then(() => deferred.resolve()),
|
|
134
|
-
)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function rxjsObservableTest<A>(init: () => RxJS.Observable<A>) {
|
|
138
|
-
return PerformanceTestCase('RxJS @7', init, (observable, deferred) => {
|
|
139
|
-
observable.subscribe({
|
|
140
|
-
complete: () => deferred.resolve(),
|
|
141
|
-
})
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function effectTsStreamTest<E, A>(init: () => EffectStream.Stream<never, E, A>) {
|
|
146
|
-
return PerformanceTestCase(
|
|
147
|
-
'Effect/Stream',
|
|
148
|
-
() => EffectStream.runDrain(init()),
|
|
149
|
-
(e, deferred) => {
|
|
150
|
-
Effect.unsafeRunPromise(e).then(() => deferred.resolve())
|
|
151
|
-
},
|
|
152
|
-
)
|
|
153
|
-
}
|
package/perf/cases/flatMap.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import * as EffectStream from '@effect/core/Stream/Stream'
|
|
2
|
-
import { pipe } from '@fp-ts/data/Function'
|
|
3
|
-
import * as M from '@most/core'
|
|
4
|
-
import * as Chunk from '@tsplus/stdlib/collections/Chunk'
|
|
5
|
-
import * as rxjs from 'rxjs'
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
array,
|
|
9
|
-
effectTsStreamTest,
|
|
10
|
-
fxTest,
|
|
11
|
-
iterations,
|
|
12
|
-
mostStreamTest,
|
|
13
|
-
rxjsObservableTest,
|
|
14
|
-
} from '../_internal.js'
|
|
15
|
-
|
|
16
|
-
import * as Stream from '@/index.js'
|
|
17
|
-
|
|
18
|
-
const nestedArray = array.map((x) => Array.from({ length: x }, (_, i) => x * 1000 + i))
|
|
19
|
-
|
|
20
|
-
const add = (x: number, y: number): number => x + y
|
|
21
|
-
|
|
22
|
-
export const name = 'flatMap ' + iterations + ' x ' + iterations + ' integers'
|
|
23
|
-
export const cases = [
|
|
24
|
-
fxTest(() =>
|
|
25
|
-
pipe(
|
|
26
|
-
Stream.fromIterable(nestedArray),
|
|
27
|
-
Stream.flatMap(Stream.fromIterable),
|
|
28
|
-
Stream.scan(0, add),
|
|
29
|
-
),
|
|
30
|
-
),
|
|
31
|
-
mostStreamTest(() =>
|
|
32
|
-
pipe(
|
|
33
|
-
M.periodic(0),
|
|
34
|
-
M.withItems(nestedArray),
|
|
35
|
-
M.chain((ns) => pipe(M.periodic(0), M.withItems(ns))),
|
|
36
|
-
M.scan(add, 0),
|
|
37
|
-
),
|
|
38
|
-
),
|
|
39
|
-
rxjsObservableTest(() =>
|
|
40
|
-
pipe(rxjs.from(nestedArray), rxjs.flatMap(rxjs.from), rxjs.scan(add, 0)),
|
|
41
|
-
),
|
|
42
|
-
effectTsStreamTest(() =>
|
|
43
|
-
pipe(
|
|
44
|
-
EffectStream.fromChunk(Chunk.from(nestedArray)),
|
|
45
|
-
EffectStream.flatMap((ns) => EffectStream.fromChunk(Chunk.from(ns))),
|
|
46
|
-
EffectStream.scan(0, add),
|
|
47
|
-
),
|
|
48
|
-
),
|
|
49
|
-
]
|
package/perf/cases/switchMap.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { pipe } from '@fp-ts/data/Function'
|
|
2
|
-
import * as M from '@most/core'
|
|
3
|
-
import * as rxjs from 'rxjs'
|
|
4
|
-
|
|
5
|
-
import { array, fxTest, iterations, mostStreamTest, rxjsObservableTest } from '../_internal.js'
|
|
6
|
-
|
|
7
|
-
import * as Stream from '@/index.js'
|
|
8
|
-
|
|
9
|
-
const nestedArray = array.map((x) => Array.from({ length: x }, (_, i) => x * 1000 + i))
|
|
10
|
-
|
|
11
|
-
const add = (x: number, y: number): number => x + y
|
|
12
|
-
|
|
13
|
-
export const name = 'switchMap ' + iterations + ' x ' + iterations + ' integers'
|
|
14
|
-
|
|
15
|
-
export const cases = [
|
|
16
|
-
fxTest(() =>
|
|
17
|
-
pipe(
|
|
18
|
-
Stream.fromIterable(nestedArray),
|
|
19
|
-
Stream.switchMap(Stream.fromIterable),
|
|
20
|
-
Stream.scan(0, add),
|
|
21
|
-
),
|
|
22
|
-
),
|
|
23
|
-
mostStreamTest(() =>
|
|
24
|
-
pipe(
|
|
25
|
-
M.periodic(0),
|
|
26
|
-
M.withItems(nestedArray),
|
|
27
|
-
M.map((ns) => pipe(M.periodic(0), M.withItems(ns))),
|
|
28
|
-
M.switchLatest,
|
|
29
|
-
M.scan(add, 0),
|
|
30
|
-
),
|
|
31
|
-
),
|
|
32
|
-
rxjsObservableTest(() =>
|
|
33
|
-
pipe(rxjs.from(nestedArray), rxjs.switchMap(rxjs.from), rxjs.scan(add, 0)),
|
|
34
|
-
),
|
|
35
|
-
// Effect Stream is pull-based and does not offer switchMap
|
|
36
|
-
]
|
package/perf/readme-base.md
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# Performance Tests
|
|
2
|
-
|
|
3
|
-
Here is the latest output of the perf test suite run on a 2020 13" macbook pro.
|
|
4
|
-
It is worth noting that all the tests utilize entirely synchronous workflows, and thus
|
|
5
|
-
have a great favoring towards Most.js/RxJS in terms of raw performance overhead.
|
|
6
|
-
|
|
7
|
-
If you need strict performance over push-based streams, definitely use Most.js, but if you are
|
|
8
|
-
interested in the superpowers of `Effect`, and are interested in using push-based streams as a means
|
|
9
|
-
of orchestration `Fx` is still a great bet as it won't often be your bottleneck.
|
|
10
|
-
|
|
11
|
-
If you need strict performance no matter what, none of these abstractions should be utilized at all.
|
|
12
|
-
|
|
13
|
-
## Test Results
|
|
14
|
-
|
package/perf/readme.md
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Performance Tests
|
|
2
|
-
|
|
3
|
-
Here is the latest output of the perf test suite run on a 2020 13" macbook pro.
|
|
4
|
-
It is worth noting that all the tests utilize entirely synchronous workflows, and thus
|
|
5
|
-
have a great favoring towards Most.js/RxJS in terms of raw performance overhead.
|
|
6
|
-
|
|
7
|
-
If you need strict performance over push-based streams, definitely use Most.js, but if you are
|
|
8
|
-
interested in the superpowers of `Effect`, and are interested in using push-based streams as a means
|
|
9
|
-
of orchestration `Fx` is still a great bet as it won't often be your bottleneck.
|
|
10
|
-
|
|
11
|
-
If you need strict performance no matter what, none of these abstractions should be utilized at all.
|
|
12
|
-
|
|
13
|
-
## Test Results
|
|
14
|
-
|
|
15
|
-
### filter -> map -> scan 10000 integers
|
|
16
|
-
| Library | Ops/sec | ± | Samples |
|
|
17
|
-
| --------------|------------|--------|---------|
|
|
18
|
-
| Fx | 3801.43 | 5.81% | 82 |
|
|
19
|
-
| @most/core | 384094.58 | 12.96% | 64 |
|
|
20
|
-
| RxJS @7 | 2255.04 | 12.60% | 87 |
|
|
21
|
-
| Effect/Stream | 91.50 | 29.12% | 41 |
|
|
22
|
-
-------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
### flatMap 10000 x 10000 integers
|
|
25
|
-
| Library | Ops/sec | ± | Samples |
|
|
26
|
-
| --------------|------------|--------|---------|
|
|
27
|
-
| Fx | 6474.91 | 11.85% | 76 |
|
|
28
|
-
| @most/core | 276654.02 | 19.33% | 46 |
|
|
29
|
-
| RxJS @7 | 10322.11 | 52.99% | 77 |
|
|
30
|
-
| Effect/Stream | 2.29 | 124.34% | 9 |
|
|
31
|
-
-------------------------------------------------------
|
|
32
|
-
|
|
33
|
-
### switchMap 10000 x 10000 integers
|
|
34
|
-
| Library | Ops/sec | ± | Samples |
|
|
35
|
-
| --------------|------------|--------|---------|
|
|
36
|
-
| Fx | 4759.84 | 13.52% | 63 |
|
|
37
|
-
| @most/core | 300663.64 | 14.57% | 55 |
|
|
38
|
-
| RxJS @7 | 16469.83 | 2.63% | 75 |
|
|
39
|
-
-------------------------------------------------------
|
|
40
|
-
|
package/perf/runFullSuite.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process'
|
|
2
|
-
import * as fs from 'node:fs'
|
|
3
|
-
import { basename, dirname, join } from 'node:path'
|
|
4
|
-
import { fileURLToPath } from 'node:url'
|
|
5
|
-
|
|
6
|
-
const currentDir = dirname(fileURLToPath(import.meta.url))
|
|
7
|
-
const casesDir = join(currentDir, '/cases')
|
|
8
|
-
|
|
9
|
-
export const runPerfTestPath = join(currentDir, 'runPerfTest.ts')
|
|
10
|
-
export const readmeBasePath = join(currentDir, 'readme-base.md')
|
|
11
|
-
export const readmePath = join(currentDir, 'readme.md')
|
|
12
|
-
export const fileNames = fs.readdirSync(casesDir).map((x) => basename(x))
|
|
13
|
-
|
|
14
|
-
let readmeContent = fs.readFileSync(readmeBasePath, 'utf-8').toString()
|
|
15
|
-
|
|
16
|
-
for (const fileName of fileNames) {
|
|
17
|
-
console.log('Running', 'cases/' + basename(fileName), '\n')
|
|
18
|
-
|
|
19
|
-
const { stdout } = spawnSync(`node`, [
|
|
20
|
-
`--loader`,
|
|
21
|
-
`@esbuild-kit/esm-loader`,
|
|
22
|
-
`${runPerfTestPath}`,
|
|
23
|
-
`${fileName}`,
|
|
24
|
-
])
|
|
25
|
-
|
|
26
|
-
const output = stdout.toString()
|
|
27
|
-
|
|
28
|
-
console.log(output)
|
|
29
|
-
|
|
30
|
-
readmeContent += output
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (fs.existsSync(readmePath)) {
|
|
34
|
-
fs.unlinkSync(readmePath)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
fs.writeFileSync(readmePath, readmeContent)
|
package/perf/runPerfTest.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import process from 'node:process'
|
|
2
|
-
|
|
3
|
-
import { runPerformanceTest } from './_internal.js'
|
|
4
|
-
|
|
5
|
-
const fileName = process.argv.slice(2).filter((x) => x.trim() !== '--')[0]
|
|
6
|
-
|
|
7
|
-
async function main() {
|
|
8
|
-
const { name, cases } = await import(
|
|
9
|
-
`./cases/${fileName}${fileName.endsWith('.ts') ? '' : '.ts'}`
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
runPerformanceTest({ name, cases }, () => process.exit(0))
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
main().catch((error) => {
|
|
16
|
-
console.error(error)
|
|
17
|
-
process.exit(1)
|
|
18
|
-
})
|