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

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

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

Vladimir Agafonkin

July 19, 2019
Tweet

More Decks by Vladimir Agafonkin

Other Decks in Programming

Transcript

  1. Vladimir Agafonkin
    ⚡fast by default:

    algorithmic optimization in practice

    View Slide

  2. rain.in.ua

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. obsessed with
    performance⚡

    View Slide

  8. ❤❤❤
    open source

    View Slide

  9. 1.find a narrowly scoped task
    2.⚡make the world’s fastest and
    simplest JavaScript library for it
    3. repeat

    View Slide

  10. Leaflet, mapbox-gl-js, mapbox-gl-native, earcut, earcut.hpp,
    pixelmatch, rbush, rbush-knn, kdbush, kdbush.hpp,
    geokdbush, flatbush, geoflatbush, concaveman, pbf,
    supercluster, supercluster.hpp, dobbyscan, delaunator,

    d3-delaunay, delaunator-rs, linematch, lineclip, geojson-vt,
    geojson-vt-cpp, potpack, simplify-js, cheap-ruler, polylabel,
    tinyqueue, flatqueue, tile-cover, which-polygon, quickselect,
    webgl-wind, suncalc, flamebearer, simple-statistics, tiny-sdf,
    geobuf, tile-reduce, geojson.hpp, geometry.hpp, tile-decorator,
    mbtiles-extracts, simpleheat, road-orientation-map,
    rollup, magic-string, sourcemap-codec, binary-split

    View Slide

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

    View Slide

  12. github.com/mapbox/potpack

    View Slide

  13. ~
    mapbox/geojson-vt (JS)
    mapbox/geojson-vt-cpp (C++)

    View Slide

  14. mapbox/supercluster (JS)
    mapbox/supercluter-hpp (C++)

    View Slide

  15. mapbox/supercluster (JS)
    mapbox/supercluter-hpp (C++)

    View Slide

  16. spatial indices

    View Slide

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

    View Slide

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

    View Slide

  19. View Slide

  20. github.com/d3/d3-delaunay

    View Slide

  21. github.com/mapbox/delaunator

    View Slide

  22. 1.delaunay: 150s
    2.delaunay-fast: 117s
    3.faster-delaunay: 5s
    4.delaunator: 1s⚡
    5.delaunator-cpp: 0.9s
    6.delaunator-rs: 0.9s
    triangulating 1 million points in JS

    View Slide

  23. math education? %

    View Slide

  24. View Slide

  25. why learn
    algorithms?

    View Slide

  26. Vladimir Agafonkin
    a super-silly introduction

    to algorithms

    View Slide

  27. View Slide

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

    View Slide

  29. View Slide

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

    View Slide

  31. slow code:
    code that does
    unnecessary work

    View Slide

  32. View Slide

  33. Rollup

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  37. your code
    dependencies

    View Slide

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

    View Slide

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

    View Slide

  40. algorithmic complexity:
    a way to describe
    how performance scales
    with input size

    View Slide

  41. 1. O(1) — 1
    2. O(n) — 1000
    3. O(n2) — 1,000,000
    4. O(n3) — 1,000,000,000
    (n = 1000)

    View Slide

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

    View Slide

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

    View Slide

  44. O(n2) — terribly 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 Slide

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

    View Slide

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

    View Slide

  47. accidentallyquadratic.tumblr.com

    View Slide

  48. 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 Slide

  49. 1. O(1) — instant
    2. O(n) — suspicious
    3. O(n2) — terribly slow
    4. O(n3) — throw out

    View Slide

  50. 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 Slide

  51. 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 Slide

  52. View Slide

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

    View Slide

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

    View Slide

  55. for (const b of items) {
    a.slice().concat(b).map(foo)
    .filter(bar).reduce(bla, 0);
    }

    View Slide

  56. functional
    programming!
    immutability!
    reactivity!
    damn
    allocations

    View Slide

  57. function foo() {

    return [x, y];
    }
    const [x, y] = foo();

    View Slide

  58. Rich-Harris/sourcemap-codec#71
    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) {
    ...
    }
    }
    }

    View Slide

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

    View Slide

  60. O(log n)
    logarithmic complexity

    View Slide

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

    View Slide

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

    View Slide

  63. binary search
    o(log n)

    View Slide

  64. View Slide

  65. View Slide

  66. View Slide

  67. View Slide

  68. View Slide

  69. View Slide

  70. rollup/rollup#2228

    40x faster

    View Slide

  71. 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 Slide

  72. slow slow slow
    algorithmic optimization

    View Slide

  73. organize data
    algorithmic optimization

    View Slide

  74. algorithmic optimization:
    do more at the beginning

    to do less every next time

    View Slide

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

    View Slide

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

    View Slide

  77. sort so that all items in the left are smaller
    o(n)
    github.com/mourner/quickselect
    39, 28, 28, 33, 21, 12, 22, 50, 53, 56, 59, 65, 90, 77, 95

    View Slide

  78. github.com/mourner/kdbush

    View Slide

  79. priority queue:
    push: o(1)
    top: o(1)
    pop: o(log n)
    github.com/mourner/tinyqueue
    github.com/mourner/flatqueue

    View Slide

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

    View Slide

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

    View Slide

  82. people take
    established code
    for granted

    View Slide

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

    View Slide

  84. 1. learn how things work under the hood

    2. don’t hesitate to dig inside frameworks

    3. contribute to open source

    4. don’t be afraid to reinvent the wheel

    5. simplify your code constantly

    6. practice optimization, and you’ll learn how
    to write fast code from the start

    View Slide

  85. Gràcies
    agafonkin.com

    View Slide