Skip to content

Commit 94745e7

Browse files
authored
test: benchmarking iter-ops (#77)
* test: benchmarking iter-ops * docs: updating benchmark results
1 parent 6c5d29a commit 94745e7

4 files changed

Lines changed: 59 additions & 22 deletions

File tree

README.md

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ Here a some results we got comparing to another similar options on node 22:
1919

2020
| Library | Ops/sec | Margin | Ran samples |
2121
| ------- | ------- | ----------- | ----------- |
22-
| **fluent** | 473 | ±0.99% | 91 |
23-
| [iterare](https://www.npmjs.com/package/iterare ) | 438 | ±0.39% | 88 |
24-
| native builtin iterator | 312 | ±0.41% | 93 |
25-
| [iter-tools](https://www.npmjs.com/package/iter-tools-es ) | 242 | ±0.39% | 88 |
26-
| [rxjs](https://www.npmjs.com/package/rxjs) | 197 | ±1.02% | 84 |
27-
| native generators | 156 | ±0.57% | 81 |
28-
| native array operations chain | 68.95 | ±1.69% | 71 |
22+
| **fluent** | 494 | ±1.43% | 91 |
23+
| [iterare](https://www.npmjs.com/package/iterare) | 436 | ±1.10% | 92 |
24+
| [iter-ops](https://www.npmjs.com/package/iter-ops) | 376 | ±0.21% | 91 |
25+
| native builtin iterator | 326 | ±0.21% | 92 |
26+
| [iter-tools](https://www.npmjs.com/package/iter-tools-es) | 243 | ±0.17% | 88 |
27+
| [rxjs](https://www.npmjs.com/package/rxjs) | 197 | ±0.49% | 83 |
28+
| native generators | 155 | ±0.83% | 81 |
29+
| native array operations chain | 63.71 | ±2.79% | 66 |
2930

3031
Notice that what we call native builtin ierator is the [iterator helper](https://v8.dev/features/iterator-helpers), recently released in the NodeJs 22, so due to some strategies we use, we can achieve a performance even better than the helper implemented in the V8 Engine!
3132
You can check the benchmark code [here](./test-benchmark/general-benchmark.spec.ts)
@@ -51,9 +52,9 @@ const result = flattedList
5152
This code looks fluent and easy to read, but it is severe to performance.
5253
That's because each operation do a complete iteration over the array, generating a new one! It can cause serious memory and cpu consumption, so, such practice is not encouraged.
5354
So, to solve this, you can write an equivalent code which will solve everything in with two chained loops. This will give you the best performance possible, but can result in a code harder to maintain.
54-
And that's where **fast-iterable** comes in!
55+
And that's where **fluent-iterable** comes in!
5556

56-
With **fast-iterable**, you can do the same operation with the following code:
57+
With **fluent-iterable**, you can do the same operation with the following code:
5758

5859
``` typescript
5960
const result = fluent(list)
@@ -68,7 +69,7 @@ const result = fluent(list)
6869
```
6970

7071
Pretty simple, right? With this code, you'll do exactly two chained loops, exactly as the vanilla solution described above!
71-
**fast-iterable** takes advantage of the Iterable and AsyncIterable contracts to achieve this, but it goes beyond. It uses a special library called **augmentative-iterables** that we developed as the core engine of the iterations. This library is focused exclusively in performance, so, with it, we achieved a processing time very close to the vanilla solution above!
72+
**fluent-iterable** takes advantage of the Iterable and AsyncIterable contracts to achieve this, but it goes beyond. It uses a special library called **augmentative-iterables** that we developed as the core engine of the iterations. This library is focused exclusively in performance, so, with it, we achieved a processing time very close to the vanilla solution above!
7273
Comparing to iterations using **for of**, the native way to iterate over iterables of JavaScript, we achieved a result 50% faster!
7374

7475
## Doesn't it what rxjs do?
@@ -85,20 +86,20 @@ Think of RxJS as Lodash for events.
8586
That's it. Rxjs is focused primarily in event handling.
8687
Over that, some key differences can be pointed out:
8788

88-
* *A previous operation of rxjs doesn't stop when some next operation stops, while with **fast-iterable** it does.*
89-
That's because, with rxjs you can chain multiple operations parallel after one, which makes sense for event handling. With **fast-iterable**, on the other hand, you can only have, normally, a straight line of operations and,f no matter what operation break the iteration, everything stops.
90-
* *With rxjs, a previous operation doesn't wait for a async next operation to end before go to the next step, while with **fast-iterable** it does.*
89+
* *A previous operation of rxjs doesn't stop when some next operation stops, while with **fluent-iterable** it does.*
90+
That's because, with rxjs you can chain multiple operations parallel after one, which makes sense for event handling. With **fluent-iterable**, on the other hand, you can only have, normally, a straight line of operations and,f no matter what operation break the iteration, everything stops.
91+
* *With rxjs, a previous operation doesn't wait for a async next operation to end before go to the next step, while with **fluent-iterable** it does.*
9192
Again, rxjs is focused on events. When dealing with event, you just want to emit them as fast as possible. With a simple iteration, though, you want to make sure that the whole chain of steps is concluded before advancing to the next one.
9293

9394
So, as you see, regardless some similarities, there're some pretty important differences between them and those are libraries focused on quite different problems.
9495

9596
## Usage
9697

97-
**fast-iterable** have some neat operations already implements. If you want to Click here for the [Full API Reference](https://github.com/Codibre/fluent-iterable/blob/master/docs/README.md).
98+
**fluent-iterable** have some neat operations already implements. If you want to Click here for the [Full API Reference](https://github.com/Codibre/fluent-iterable/blob/master/docs/README.md).
9899

99100
### Basics
100101

101-
ECMAScript introduced support for [iterables and generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) with version ES6 and their [asynchronous counterparts](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator) with version ES2018. It has introduced an abstraction over sequential iterators (arrays, maps, generators, etc), enabling us to implement solutions regardless of the actual type of the iterable collection. It is especially powerful when using in tandem with generator functions to avoid storing all items in memory when its avoidable. The API provided by ***fast-iterable*** reads the elements of the underlying iterable only when needed and stops reading elements as soon as the result is determined.
102+
ECMAScript introduced support for [iterables and generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) with version ES6 and their [asynchronous counterparts](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator) with version ES2018. It has introduced an abstraction over sequential iterators (arrays, maps, generators, etc), enabling us to implement solutions regardless of the actual type of the iterable collection. It is especially powerful when using in tandem with generator functions to avoid storing all items in memory when its avoidable. The API provided by ***fluent-iterable*** reads the elements of the underlying iterable only when needed and stops reading elements as soon as the result is determined.
102103

103104
To get started with the fluent API, you need to translate the iterable (can be any object with [Symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) [iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) or [asyncIterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator) defined) into either a **FluentIterable** using **fluent()** or a **FluentAsyncIterable** using **fluentAsync()**.
104105

@@ -109,7 +110,7 @@ import {
109110
fluentAsync,
110111
FluentIterable,
111112
FluentAsyncIterable,
112-
} from '**fast-iterable**';
113+
} from '**fluent-iterable**';
113114

114115
const iterableOfArray: FluentIterable<number> = fluent([3, 1, 8, 6, 9, 2]);
115116

@@ -212,7 +213,7 @@ You can see a list of many advanced examples for **fluent** clicking [here!](adv
212213
#### Playing with Fibonacci generator
213214

214215
``` typescript
215-
import { fluent } from '**fast-iterable**';
216+
import { fluent } from '**fluent-iterable**';
216217

217218
function* naiveFibonacci(): Iterable<number> {
218219
yield 0;
@@ -259,7 +260,7 @@ console.log(
259260
#### Playing with object arrays
260261

261262
``` typescript
262-
import { fluent } from '**fast-iterable**';
263+
import { fluent } from '**fluent-iterable**';
263264

264265
enum Gender {
265266
Male = 'Male',
@@ -342,7 +343,7 @@ console.log(
342343

343344
``` typescript
344345
import fetch from 'node-fetch';
345-
import { fluentAsync, Pager } from '**fast-iterable**';
346+
import { fluentAsync, Pager } from '**fluent-iterable**';
346347

347348
interface Data {
348349
id: number;
@@ -371,7 +372,7 @@ fluentAsync(depaginate(pager))
371372
### Doing an inner join between two iterables:
372373

373374
``` typescript
374-
import { fluent, identity } from '**fast-iterable**';
375+
import { fluent, identity } from '**fluent-iterable**';
375376

376377
const genders = [
377378
{ code: 'm', description: 'male' },
@@ -422,7 +423,7 @@ fluent(genders)
422423
``` typescript
423424
import { DynamoDB } from 'aws-sdk';
424425
import { Key } from 'aws-sdk/clients/dynamodb';
425-
import { depaginate, fluentAsync, Pager } from '**fast-iterable**';
426+
import { depaginate, fluentAsync, Pager } from '**fluent-iterable**';
426427

427428
async function *scan<TData>(
428429
input: DynamoDB.DocumentClient.ScanInput
@@ -469,7 +470,7 @@ The solution used for this problems was 90% inspired in the [fraxken combine-asy
469470
You can add custom methods to the FluentIterable and FluentAsyncIterable using the *extend* and *extendAsync* utilities. Here is a practical example of how to:
470471

471472
``` TypeScript
472-
declare module '**fast-iterable**' {
473+
declare module '**fluent-iterable**' {
473474
import { extendAsync } from '../src';
474475

475476
interface FluentAsyncIterable<T> {

package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
"expect": "^29.7.0",
119119
"for-emit-of": "^1.4.0",
120120
"husky": "^9.1.6",
121+
"iter-ops": "^3.5.0",
121122
"iter-tools-es": "^7.5.3",
122123
"iterare": "^1.2.1",
123124
"jest": "^29.7.0",

test/benchmark/general-benchmark.spec.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const rxjs = require('rxjs');
33
const rxjsOp = require('rxjs/operators');
44
const { iterate } = require('iterare');
55
const iterTools = require('iter-tools-es');
6+
const iterOps = require('iter-ops');
67
const Benchmark = require('benchmark');
78

89
const ITEMS = 100000;
@@ -157,6 +158,29 @@ benchmarkSuite
157158
iterTools.forEach((x) => x.join(',')),
158159
);
159160
})
161+
.add('iter-ops', () => {
162+
return Array.from(iterOps.pipe(
163+
interval(1, ITEMS),
164+
iterOps.map((x) => x * MULTIPLIER1),
165+
iterOps.map((x) => x * MULTIPLIER2),
166+
iterOps.map((x) => x / MULTIPLIER2),
167+
iterOps.map((x) => x * MULTIPLIER2),
168+
iterOps.map((x) => x / MULTIPLIER2),
169+
iterOps.map((x) => x * MULTIPLIER2),
170+
iterOps.filter((x) => x % QUOTIENT === 0),
171+
iterOps.map((x) => x / MULTIPLIER2),
172+
iterOps.map((x) => x * MULTIPLIER2),
173+
iterOps.map((x) => x / MULTIPLIER2),
174+
iterOps.map((x) => x * MULTIPLIER2),
175+
iterOps.map((x) => x / MULTIPLIER2),
176+
iterOps.map((x) => x * MULTIPLIER2),
177+
iterOps.map((x) => x / MULTIPLIER2),
178+
iterOps.map((x) => x * MULTIPLIER2),
179+
iterOps.map((x) => interval2(x, x + FLAT_FACTOR)),
180+
iterOps.take(TAKE),
181+
iterOps.last((x) => x.join(',')),
182+
))[0];
183+
})
160184
.add('iterare', () => {
161185
iterate(interval(1, ITEMS))
162186
.map((x) => x * MULTIPLIER1)

0 commit comments

Comments
 (0)