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

Fast by default: everyday algorithmic thinking for developers

Fast by default: everyday algorithmic thinking for developers

We've grown to rely on high level frameworks so much that we no longer know how computers work. So when we're stuck with bad performance, simply changing frameworks won't help — you have to understand what's going on under the hood, and what to do with it. That's what algorithmic thinking is about.

Does the app have a bottleneck, and how do you identify it? Does this code really have to be slow, or is it doing unnecessary work? How do we achieve the same result while doing less? Can we delay a part of the work? Can we order the data differently for faster processing? When you practice algorithmic thinking, answering those questions becomes second nature, and eventually you learn to write code that's fast from the start, by default. So let me introduce you to algorithms again, from scratch, in a way that is useful for your everyday work.

Vladimir Agafonkin

September 12, 2018
Tweet

More Decks by Vladimir Agafonkin

Other Decks in Programming

Transcript

  1. ⚡fast by default:
    everyday algorithmic
    thinking for developers
    Vladimir Agafonkin

    View full-size slide

  2. obsessed with
    performance

    View full-size slide

  3. ❤❤❤
    open source

    View full-size slide

  4. 1. Find a narrowly scoped task
    2. ⚡ Make the world’s fastest and simplest
    JavaScript library for it
    3. Back to step 1

    View full-size slide

  5. Leaflet, mapbox-gl-js, mapbox-gl-native, earcut, earcut.hpp,
    rbush, rbush-knn, kdbush, kdbush.hpp, geokdbush,
    flatbush, geoflatbush, concaveman, supercluster,
    supercluster.hpp, dobbyscan, delaunator, d3-delaunay,
    linematch, lineclip, pixelmatch, simplify-js, cheap-ruler,
    polylabel, tinyqueue, flatqueue, tile-cover, which-polygon,
    quickselect, simple-statistics, tiny-sdf, geojson-vt,
    potpack, geojson-vt-cpp, geobuf, pbf, tile-reduce,
    geojson.hpp, geometry.hpp, tile-decorator, mbtiles-
    extracts, webgl-wind, suncalc, flamebearer, simpleheat,
    binary-split, magic-string, polysnap, rollup, sourcemap-codec

    View full-size slide

  6. github.com/mapbox/delaunator

    View full-size slide

  7. github.com/d3/d3-delaunay

    View full-size slide

  8. 1.delaunay: 150s
    2.delaunay-fast: 117s
    3.faster-delaunay: 5s
    4.delaunator: 1.3s⚡
    triangulating 1 million points in JavaScript

    View full-size slide

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

    View full-size slide

  10. spatial indices

    View full-size slide

  11. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  15. github.com/mapbox/webgl-wind

    View full-size slide

  16. github.com/mapbox/potpack

    View full-size slide

  17. mathematical
    background? %

    View full-size slide

  18. why understand
    algorithms?

    View full-size slide

  19. 99% of performance bottlenecks
    are of algorithmic nature

    View full-size slide

  20. slow code:
    code which does
    unnecessary work

    View full-size slide

  21. productive laziness:
    the art of avoiding
    unnecessary work

    View full-size slide

  22. algorithmic thinking:
    the art of avoiding
    unnecessary work
    by a machine

    View full-size slide

  23. your app
    frameworks

    View full-size slide

  24. rollup/rollup#2062
    rollup: map = magicString.generateMap()
    rollup: obj = decode(map)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  27. 1. identify a bottleneck
    2. find out why it’s a bottleneck
    3. optimize the bottleneck
    performance optimization:
    requires understanding algorithms

    View full-size slide

  28. algorithmic complexity:
    and where to look for
    unnecessary work
    a way to describe
    how performance scales
    with input size

    View full-size slide

  29. O(1)
    O(log n)
    O(n)
    O(n log n)
    O(n^2)
    O(n^3)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  32. 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 full-size slide

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

    View full-size slide

  34. 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 full-size slide

  35. accidentallyquadratic.tumblr.com

    View full-size slide

  36. O(n3) — unbearable, throw away
    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

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

    View full-size slide

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

    View full-size slide

  39. чорти б мене побрали!!!
    хай йому грець!!
    дідько!
    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

  40. 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

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

    View full-size slide

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

    View full-size slide

  43. for (const b of items) {
    a.slice().concat(b).map(foo)
    .filter(bar).reduce(bla, 0);
    }
    allocate memory and
    throw it away immediately (4 times)

    View full-size slide

  44. functional
    programming!
    immutability!
    reactivity!
    damn
    allocations

    View full-size slide

  45. foo.forEach(a => {
    bar.forEach(b => {
    baz.forEach(c => {
    ...
    });
    });
    });

    View full-size slide

  46. JIT-optimizing JS engines
    don’t always understand
    what you want

    View full-size slide

  47. write predictable code
    so that the engine
    doesn’t have to guess

    View full-size slide

  48. 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 full-size slide

  49. 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) { // ";"
    ...
    2.7x faster decoding

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  52. binary search
    o(log n)

    View full-size slide

  53. rollup/rollup#2228

    40x faster

    View full-size slide

  54. 1. O(n) → O(log n)
    2. O(n log n) → O(n)
    3. O(n2) → O(n log n)
    4. O(n3) — throw away
    algorithmic optimization:

    View full-size slide

  55. slow slow slow
    algorithmic optimization:

    View full-size slide

  56. data processing
    algorithmic optimization:

    View full-size slide

  57. do more at the beginning

    to do less every next time
    algorithmic optimization:

    View full-size slide

  58. data structures:
    ways to organize data

    to search and update it efficiently

    View full-size slide

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

    View full-size slide

  60. sorting an array:
    o(n log n)

    View full-size slide

  61. sorting an array so that
    smallest k items come first:
    o(n)
    github.com/mourner/quickselect

    View full-size slide

  62. github.com/mourner/kdbush

    View full-size slide

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

    View full-size slide

  64. • how often do you insert items?
    • how often do you remove items?
    • how often do you access items,
    and how?
    data structures:

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  67. how I read

    academic papers

    and reference code

    View full-size slide

  68. 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 full-size slide