Skip to content

Make it run faster. #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 50 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
ec494bb
:wrench: config(hooks): Explicit config path in commit-msg.
make-github-pseudonymous-again Oct 14, 2021
2eabf53
:wrench: config(xo): Use @babel/eslint-parser.
make-github-pseudonymous-again Oct 14, 2021
0fb92a7
:test_tube: test(api): Exhaustive testing of from on small iterables.
make-github-pseudonymous-again Oct 14, 2021
8a73b4c
:package: deps: Relocate deps specific to profiling and benchmarking.
make-github-pseudonymous-again Oct 14, 2021
dc90704
:construction: progress(benchmark): Add fromIterable benchmark.
make-github-pseudonymous-again Oct 14, 2021
2232458
:construction: progress(profiling): Add more profiling loops.
make-github-pseudonymous-again Oct 14, 2021
af7387a
:umbrella: test(concat/append): Prevent regression.
make-github-pseudonymous-again Oct 14, 2021
fee323c
:microscope: test(nodes,from,concat,append,prepend): Increase coverage.
make-github-pseudonymous-again Oct 14, 2021
a7dad80
:test_tube: test: Sketch debug API to avoid private props direct access.
make-github-pseudonymous-again Oct 15, 2021
196675c
:construction: progress(benchmark/fromIterable): Add Array construction.
make-github-pseudonymous-again Oct 1, 2021
2fd09fc
:umbrella: test(append): Prevent regression on append's pureness.
make-github-pseudonymous-again Oct 1, 2021
d299223
:recycle: refactor(profile): Write better code and add analysis script.
make-github-pseudonymous-again Oct 4, 2021
bc858a3
:construction: progress: More benchmarks.
make-github-pseudonymous-again Oct 4, 2021
89f4ab0
:test_tube: test: Do not pull source in fixtures.
make-github-pseudonymous-again Oct 4, 2021
03c005e
:construction: progress: Add code to bench and profile split and iter…
make-github-pseudonymous-again Oct 4, 2021
2c891cf
:microscope: test: Increase coverage.
make-github-pseudonymous-again Oct 15, 2021
7fc67b2
:construction: progress(profile/iterator): Fix timer title.
make-github-pseudonymous-again Oct 15, 2021
a2ee7ff
:construction: progress(benchmark/table): Add filter option.
make-github-pseudonymous-again Oct 14, 2021
0646b31
:bicyclist: perf: Inline loopy code inside core/concatenate/nodes.
make-github-pseudonymous-again Aug 31, 2021
8a77722
:recycle: refactor(_from_small_list): Do not use default case.
make-github-pseudonymous-again Oct 14, 2021
814bdc0
:recycle: refactor(Two): Implement node method.
make-github-pseudonymous-again Oct 14, 2021
d01ee6a
:broom: cleaning(Node2,Node3): Remove dead code.
make-github-pseudonymous-again Oct 14, 2021
63523e2
:bicyclist: perf(Three): Store computed measure in digit in node method.
make-github-pseudonymous-again Oct 14, 2021
02a9720
:bicyclist: perf(concat): Exploit implicit prototype branching.
make-github-pseudonymous-again Oct 14, 2021
3156698
:bicyclist: perf(from): Use concatenation of medium-sized trees.
make-github-pseudonymous-again Oct 14, 2021
d54d8da
:bicyclist: perf(append): Skip one tree level.
make-github-pseudonymous-again Oct 14, 2021
960963f
:adhesive_bandage: fix(Deep#append): Make it work.
make-github-pseudonymous-again Oct 14, 2021
be4f1f3
:recycle: refactor: Add assertions and remove unreachable code.
make-github-pseudonymous-again Oct 14, 2021
909d5fa
:recycle: refactor(Deep): Check all arguments in constructor.
make-github-pseudonymous-again Oct 14, 2021
b10d0bb
:bicyclist: perf: Exploit property mangling to reduce bundle size.
make-github-pseudonymous-again Oct 15, 2021
0f9bd6d
:bicyclist: perf: Disable function inlining as it does not work very …
make-github-pseudonymous-again Sep 30, 2021
b3b301a
:bicyclist: perf(split): Avoid creating temporary arrays.
make-github-pseudonymous-again Sep 30, 2021
e66c944
:bicyclist: perf(_fill_right): Simplify! 15% magic speed-up.
make-github-pseudonymous-again Sep 30, 2021
2edbf7a
:bicyclist: perf(append): Avoid recording intermediate states.
make-github-pseudonymous-again Oct 1, 2021
118fb83
:bicyclist: perf(from): Use much more efficient construction by pushing.
make-github-pseudonymous-again Oct 4, 2021
542f69d
:bicyclist: perf(prepend): Prepend by concatenation instead of consing.
make-github-pseudonymous-again Oct 4, 2021
9033dfb
:broom: cleaning(append/prepend): Remove unused code.
make-github-pseudonymous-again Oct 4, 2021
c19b59b
:bicyclist: perf(cache): Avoid trashing the GC in rare cases.
make-github-pseudonymous-again Oct 4, 2021
b4af917
:bicyclist: perf: Better lazy eval, avoid trashing GC with empty trees.
make-github-pseudonymous-again Oct 15, 2021
c6c53d7
:recycle: refactor(Deep): Simplify #append.
make-github-pseudonymous-again Oct 4, 2021
86dd5e5
:bicyclist: perf(Four): Exploit other._node.
make-github-pseudonymous-again Oct 15, 2021
239ad11
:broom: cleaning: Remove Tree#_copy_spine.
make-github-pseudonymous-again Oct 15, 2021
3dbdfb8
:adhesive_bandage: fix(Lazy): Define Lazy#prepend.
make-github-pseudonymous-again Oct 15, 2021
076ee35
:bicyclist: perf(iterator/reversed): 5-6x faster iterators.
make-github-pseudonymous-again Oct 15, 2021
19846b3
:bicyclist: perf(DeepIterator): Avoid creating many temporary arrays.
make-github-pseudonymous-again Oct 14, 2021
3a97164
:bicyclist: perf(DeepIterator): Replace instanceof checks by proto.
make-github-pseudonymous-again Oct 14, 2021
4e24c9c
:bicyclist: perf: Switch on small list length's last two bits.
make-github-pseudonymous-again Oct 14, 2021
d9681d8
:recycle: refactor: Remove empty Digit base class.
make-github-pseudonymous-again Oct 14, 2021
2e2b620
:recycle: refactor(nodes): Use more precise imports.
make-github-pseudonymous-again Oct 14, 2021
539c048
:books: docs(Three): Better wording in _UNSAFE_push NOTE.
make-github-pseudonymous-again Oct 14, 2021
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
61 changes: 61 additions & 0 deletions _benchmark/_fixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import fs from 'fs';
import process from 'process';

export function packageInfo(packageName) {
const file = `node_modules/${packageName}/package.json`;
const raw = fs.readFileSync(file);
const json = JSON.parse(raw);
return json;
}

export const load = async (path) => {
const exports = await import(path);
const stats = fs.statSync(path);
const mtime = stats.mtime;
const version = mtime.toISOString();

return {
version,
exports,
};
};

const distPath = (name) => `../dist/index.${name}`;

export const dist = (name) => ({
name,
async load() {
const path = distPath(name);
const {version, exports} = await load(path);
return {
name,
version,
exports,
};
},
});

export const dependency = (name) => ({
name,
async load() {
const path = name;
const exports = await import(path);
const {version} = packageInfo(name);
return {
name,
version,
exports,
};
},
});

export const object = (name, exports) => ({
name,
async load() {
return {
name,
version: process.version,
exports,
};
},
});
32 changes: 10 additions & 22 deletions _benchmark/benchmark.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
require('regenerator-runtime/runtime');
import 'regenerator-runtime/runtime.js';

const ArgumentParser = require('argparse').ArgumentParser;
const Benchmark = require('benchmark');
import {ArgumentParser} from 'argparse';
import Benchmark from 'benchmark';

const range = require('@iterable-iterator/range').range;
import {range} from '@iterable-iterator/range';

const qiao_fingertree = require('fingertree');
const fds_finger_tree = require('..');
import qiao_fingertree from 'fingertree';
import {empty, from} from '../dist/index.modern.js';

const fromArray = qiao_fingertree.fromArray;
const empty = fds_finger_tree.empty;
const from = fds_finger_tree.from;
import {FAST_COUNTER as COUNTER} from '../test/src/_fixtures.js';

const COUNTER = {
plus(a, b) {
return a + b;
},
measure(_x) {
return 1;
},
zero() {
return 0;
},
};
const fromArray = qiao_fingertree.fromArray;

const parser = new ArgumentParser();
parser.addArgument(['M']);
const args = parser.parseArgs();
parser.add_argument(['M']);
const args = parser.parse_args();

global.M = args.M;
global.COUNTER = COUNTER;
Expand Down
201 changes: 201 additions & 0 deletions _benchmark/fromIterable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import assert from 'assert';

import 'regenerator-runtime/runtime.js';
import {ArgumentParser} from 'argparse';

import {range} from '@iterable-iterator/range';
import Benchtable from 'benchtable';
import {
FAST_COUNTER as COUNTER,
measure,
measureToString,
iterableToString,
} from '../test/src/_fixtures.js';

import {dist, dependency, object} from './_fixtures.js';

const parser = new ArgumentParser();
parser.add_argument('filter');
const args = parser.parse_args();
const filter = new RegExp(args.filter, 'i');

const cjs = dist('cjs');
const module = dist('module.js');
const modern = dist('modern.js');
const list = dependency('list');
const array = object('Array', Array);

const suite = new Benchtable('Tree Construction', {isTransposed: false});

const add = async (module, fn) => {
if (!filter.test(module.name)) return;
if (!filter.test(fn.name)) return;
const {title, build} = await fn.compile(module);
suite.addFunction(
title,
(measure, iterable) => {
build(measure, iterable);
},
{
maxTime: 5,
},
);
if (!filter.test('measure')) return;
suite.addFunction(
`${title}.measure`,
(measure, iterable, expected) => {
const result = build(measure, iterable).measure();
if (result !== expected) {
throw new Error('wrong measure');
}
},
{
maxTime: 5,
},
);
};

const makeFn = ({name: fnName, build}) => ({
name: fnName,
compile: async (module) => {
const {name, version, exports} = await module.load();
return {
title: `${name} ${version} #${fnName}`,
build: build({exports}),
};
},
});

const from = makeFn({
name: 'from',
build: ({exports}) => {
const from = exports.from;
return (measure, iterable) => from(measure, iterable);
},
});

const append = makeFn({
name: 'append',
build: ({exports}) => {
const empty = exports.empty;
return (measure, iterable) => empty(measure).append(iterable);
},
});

const push = makeFn({
name: 'push',
build: ({exports}) => {
const {empty} = exports;
return (measure, iterable) => {
let l = empty(measure);
for (const x of iterable) l = l.push(x);
return l;
};
},
});

const prepend = makeFn({
name: 'prepend',
build: ({exports}) => {
const empty = exports.empty;
return (measure, iterable) => empty(measure).prepend(iterable);
},
});

const listFrom = makeFn({
name: 'from',
build: ({exports}) => {
const from = exports.from;
return (_measure, iterable) => from(iterable);
},
});

const listAppend = makeFn({
name: 'push',
build: ({exports}) => {
const {empty, append} = exports;
return (_measure, iterable) => {
let l = empty();
for (const x of iterable) l = append(x, l);
return l;
};
},
});

const arrayPush = makeFn({
name: 'push',
build: ({exports}) => {
assert(exports === Array);
return (_measure, iterable) => {
const l = [];
for (const x of iterable) l.push(x);
return l;
};
},
});

await add(cjs, from);
await add(cjs, append);
await add(cjs, prepend);
await add(cjs, push);
await add(module, from);
await add(module, append);
await add(module, prepend);
await add(module, push);
await add(modern, from);
await add(modern, append);
await add(modern, prepend);
await add(modern, push);

await add(list, listFrom);
await add(list, listAppend);
await add(array, listFrom);
await add(array, arrayPush);

const addTitle = (input) => {
const {measure, iterable} = input;
const title = `(${measureToString(measure)}, ${iterableToString(iterable)})`;
return {
...input,
title,
};
};

const addExpected = (input) => {
const expected = measure(input.measure, input.iterable);
return {
...input,
expected,
};
};

const benchmarkInputs = [
{
measure: COUNTER,
iterable: range(1000),
},
{
measure: COUNTER,
iterable: range(10_000),
},
{
measure: COUNTER,
iterable: range(100_000),
},
]
.map(addTitle)
.map(addExpected);

for (const {title, measure, iterable, expected} of benchmarkInputs) {
suite.addInput(title, [measure, iterable, expected]);
}

suite.on('cycle', (evt) => {
console.log(evt.target.name);
});

suite.on('complete', () => {
console.log(suite.table.toString());
});

suite.run();
Loading