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 Slide

  2. obsessed with
    performance

    View Slide

  3. ❤❤❤
    open source

    View 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 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 Slide

  6. github.com/mapbox/delaunator

    View Slide

  7. github.com/d3/d3-delaunay

    View Slide

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

    View Slide

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

    View Slide

  10. spatial indices

    View 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

  15. github.com/mapbox/webgl-wind

    View Slide

  16. github.com/mapbox/potpack

    View Slide

  17. View Slide

  18. mathematical
    background? %

    View Slide

  19. View Slide

  20. why understand
    algorithms?

    View Slide

  21. View Slide

  22. 99% of performance bottlenecks
    are of algorithmic nature

    View Slide

  23. slow code:
    code which does
    unnecessary work

    View Slide

  24. View Slide

  25. productive laziness:
    the art of avoiding
    unnecessary work

    View Slide

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

    View Slide

  27. your app
    frameworks

    View Slide

  28. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  33. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

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

  41. accidentallyquadratic.tumblr.com

    View Slide

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

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

    View Slide

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

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

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

  47. View Slide

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

    View Slide

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

    View Slide

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

  51. functional
    programming!
    immutability!
    reactivity!
    damn
    allocations

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

  57. View Slide

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

    View Slide

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

    View Slide

  60. binary search
    o(log n)

    View Slide

  61. View Slide

  62. View Slide

  63. View Slide

  64. View Slide

  65. View Slide

  66. View Slide

  67. rollup/rollup#2228

    40x faster

    View Slide

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

  69. slow slow slow
    algorithmic optimization:

    View Slide

  70. data processing
    algorithmic optimization:

    View Slide

  71. do more at the beginning

    to do less every next time
    algorithmic optimization:

    View Slide

  72. data structures:
    ways to organize data

    to search and update it efficiently

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  76. github.com/mourner/kdbush

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  81. how I read

    academic papers

    and reference code

    View Slide

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

  83. thank you

    View Slide