Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fast by Default: Algorithmic Optimization in Practice (dotJS 2019)

Fast by Default: Algorithmic Optimization in Practice (dotJS 2019)

Vladimir Agafonkin

December 06, 2019
Tweet

More Decks by Vladimir Agafonkin

Other Decks in Programming

Transcript

  1. Vladimir Agafonkin
    ⚡fast by default:

    algorithmic optimization in practice

    View full-size slide

  2. ⚡fast or GTFO

    View full-size slide

  3. Leaflet earcut pixelmatch delaunator rbush flatbush
    kdbush geokdbush geoflatbush geojson-vt potpack
    supercluster martini delatin cheap-ruler polylabel
    icomesh concaveman dobbyscan linematch lineclip
    simplify-js robust-predicates tinyqueue flatqueue
    which-polygon quickselect webgl-wind suncalc
    flamebearer tiny-sdf simpleheat …
    github.com/mourner/projects

    View full-size slide

  4. mapbox/earcut (JS)
    mapbox/earcut.hpp (C++)

    View full-size slide

  5. github.com/mapbox/potpack

    View full-size slide

  6. github.com/mourner/
    rbush
    kdbush
    flatbush
    rbush-knn
    geokdbush
    geoflatbush

    View full-size slide

  7. mapbox/pixelmatch (JS)
    mapbox/pixelmatch-cpp (C++)

    View full-size slide

  8. github.com/mapbox/delatin

    View full-size slide

  9. delaunay: 150s
    delaunay-fast: 117s
    faster-delaunay: 5s
    delaunator: 1s
    delaunator-cpp: 0.9s
    delaunator-rs: 0.9s
    Delaunay triangulation of
    1 million points in JavaScript:

    View full-size slide

  10. 1. find a bottleneck
    2. find out why it’s slow
    3. make it faster
    optimization:

    View full-size slide

  11. 1. find a bottleneck
    2. find out why it’s slow
    3. make it faster
    optimization:
    algorithm
    s!

    View full-size slide

  12. slow code:
    code that does
    unnecessary work

    View full-size slide

  13. rollup/rollup#2062
    rollup: map = magicString.generateMap()
    obj = decode(map)
    ";;;;EAEEA,EAAE,EAAC,CAAE;ECQY,UACC…"

    View full-size slide

  14. rollup/rollup#2062
    rollup: map = magicString.generateMap()
    magic-string: ...
    map = encode(obj)
    rollup: obj = decode(map)

    View full-size slide

  15. map = encode(obj)
    obj = decode(map)

    View full-size slide

  16. rollup/rollup#2062
    rollup: map = magicString.generateDecodedMap()
    magic-string: map = encode(obj)
    rollup: obj = decode(map)
    source maps got 40% faster

    View full-size slide

  17. algorithmic complexity:
    a measure of
    how performance scales
    with input size

    View full-size slide

  18. O(1)
    O(log n)
    O(n)
    O(n log n)
    O(n2)
    O(n3)

    View full-size slide

  19. array[0] + array[1];
    O(1) — instant

    View full-size slide

  20. for (const item of array) {
    sum += item;
    }
    O(n) — suspicious

    View full-size slide

  21. O(n2) — ridiculously slow
    for (let i = 0, n = array.length; i < n; i++) {
    for (let j = i + 1; j < n; j++) {
    sum += array[i] + array[j];
    }
    }

    View full-size slide

  22. function foo(array) {
    for (const item of array) {
    array[array.indexOf(item)] = item + 10;
    }
    }
    O(n2) — ridiculously slow

    View full-size slide

  23. function foo(array) {
    for (const item of array) {
    bar(array, item);
    }
    }
    function bar(array, item) {
    array[array.indexOf(item)] = item + 10;
    }
    O(n2) — ridiculously slow

    View full-size slide

  24. accidentallyquadratic.tumblr.com

    View full-size slide

  25. O(n3) — rewrite from scratch
    for (let i = 0, n = array.length; i < n; i++) {
    for (let j = i + 1; j < n; j++) {
    for (let k = j + 1; k < n; k++) {
    sum += array[i] + array[j];
    }
    }
    }

    View full-size slide

  26. if (j < 0) {
    if (triangles.length === 0) triangles.push([i]);
    return;
    }
    for (let n = triangles.length, a = 0; a < n; ++a) {
    let sa = triangles[a];
    if (sa[0] === j) {
    for (let b = a + 1; b < n; ++b) {
    let sb = triangles[b];
    if (sb[sb.length - 1] === i) {
    triangles.splice(b, 1);
    triangles[a] = sa = sb.concat(sa);
    return;
    }
    }
    sa.unshift(i);
    return;
    }
    d3/d3-delaunay#23

    View full-size slide

  27. array.indexOf
    array.lastIndexOf
    array.splice
    array.includes
    array.reverse
    array.every
    Object.values
    array.find
    array.some
    array.findIndex
    array.reduce
    array.reduceRight
    array.some
    Object.keys
    O(n) — suspicious

    View full-size slide

  28. array.slice
    array.concat
    array.map
    array.filter
    str.split
    suspicious allocations

    View full-size slide

  29. a.slice().concat(b).map(foo)
    .filter(bar).reduce(bla, 0);
    allocate memory
    and throw it out immediately
    (4 times)

    View full-size slide

  30. function foo() {
    ...
    return [x, y];
    }
    const [x, y] = foo();

    View full-size slide

  31. const lines = input.split(';');
    for (const line of lines) {
    const segments = line.split(',');
    for (const segment of segments) {
    const decoded = decode(segment);
    for (const i of decoded) {
    ...
    }
    }
    }
    Rich-Harris/sourcemap-codec#71

    View full-size slide

  32. for (let i = 0; i < input.length; i++) {
    const c = input.charCodeAt(i);
    if (c === 44) { // ","
    ...
    } else if (c === 59) { // ";"
    ...
    Rich-Harris/sourcemap-codec#71
    3 times faster

    View full-size slide

  33. O(log n)
    logarithmic complexity

    View full-size slide

  34. O(log n)
    O(1) O(n)
    O(log n) — blazing fast

    View full-size slide

  35. 1 1,000,000
    20
    O(log n) — blazing fast

    View full-size slide

  36. binary search
    o(log n)

    View full-size slide

  37. rollup/rollup#2228

    40x faster

    View full-size slide

  38. 1. O(n) → O(log n)
    2. O(n log n) → O(n)
    3. O(n2) → O(n log n)
    4. O(n3) — throw out
    algorithmic optimization

    View full-size slide

  39. slow slow slow
    algorithmic optimization

    View full-size slide

  40. organize data
    algorithmic optimization

    View full-size slide

  41. algorithmic optimization:
    do more at the beginning

    to do less every next time

    View full-size slide

  42. • hash map
    • hash set
    • binary search tree
    • priority heap
    • linked list
    • interval tree
    • r-tree
    • quadtree
    • kd-tree
    • …
    data structures

    View full-size slide

  43. sorted array
    o(n log n)
    ❤ favorite data structure

    View full-size slide

  44. github.com/mourner/kdbush

    View full-size slide

  45. HashTable Array
    [10, 20, 30];
    {
    '1': 10,
    '2': 20,
    '3': 30
    };
    {
    '0': 10,
    '1': 20,
    ‘2': 30
    };

    View full-size slide

  46. istanbuljs/istanbul-lib-instrument#22
    {
    '1': 10,
    '2': 20,
    '3': 30
    };
    15 times faster
    {
    '0': 10,
    '1': 20,
    ‘2': 30
    };

    View full-size slide

  47. people take
    established code
    for granted

    View full-size slide

  48. established !== best
    there is always room
    for improvement

    View full-size slide

  49. 1. learn how things work under the hood

    2. don’t be afraid to reinvent the wheel

    3. simplify your code constantly

    4. practice optimization, and you’ll learn
    how to write fast code by default

    View full-size slide

  50. agafonkin.com thank you

    View full-size slide