Thinking Outside the GIL

Thinking Outside the GIL

Have you ever written a small, elegant application that couldn't keep up with the growth of your data or user demand? Did your beautiful design end up buried in threads and locks? Did Python's very special Global Interpreter Lock make all of this an exercise in futility?

This talk is for you! With the combined powers of AsyncIO and multiprocessing, we'll redesign an old multithreaded application limited by the GIL into a modern solution that scales with the demand using only the standard library. No prior AsyncIO or multiprocessing experience required.

Presented at PyCon US 2018 in Cleveland: https://youtu.be/0kXaLh8Fz3k

9c98621c87f4f1333e0dcbb5282fa65b?s=128

John Reese

May 11, 2018
Tweet

Transcript

  1. 1.
  2. 2.

    Thinking Outside the GIL With AsyncIO and Multiprocessing John Reese

    Production Engineer, Facebook @n7cmdr
 github.com/jreese
  3. 3.

    • Global Interpreter Lock • One VM thread at a

    time • No concurrent memory access • I/O wait releases lock What’s the GIL?
  4. 4.

    • Gather ~100M data points • Process and aggregate anomalies

    • Easy to add new checks • Simple deployment • Few dependencies Stateful monitoring
  5. 5.
  6. 6.
  7. 7.
  8. 8.

    • One binary • Fetch the world • Process everything

    • Aggregate results • Thread pool for I/O #impact
  9. 9.

    • Scales in time and memory • Runtime now too

    slow • Underutilizing hardware • Ultimately limited by the GIL Not aging well
  10. 12.

    • Technically correct • Scales with number of workers •

    Complicated deployments • Communication overhead Sharding
  11. 13.
  12. 14.
  13. 15.
  14. 16.

    • Scales with CPU cores • Automatic IPC • Pool.map

    is really useful • One task per process • Beware forking, pickling Multiprocessing
  15. 18.
  16. 19.
  17. 20.
  18. 21.
  19. 22.

    • Based on futures • Faster than threads • Massive

    I/O concurrency • Processing still limited by GIL • Beware timeouts and queue length AsyncIO
  20. 24.

    • Use multiprocessing primitives • Event loop per process •

    Queues for work/results • Highly parallel workload • Need to do some plumbing Multiprocessing + AsyncIO
  21. 25.
  22. 26.
  23. 27.
  24. 28.
  25. 29.
  26. 30.
  27. 31.
  28. 32.
  29. 33.
  30. 34.
  31. 35.
  32. 36.
  33. 37.
  34. 38.
  35. 39.
  36. 40.
  37. 41.
  38. 42.
  39. 43.
  40. 44.

    • Minimize what you pickle • Prechunk work items •

    Aggregate results in the child • Use map/reduce Considerations
  41. 48.

    • Simple implementation • Emulates multiprocessing API • One shot

    or process pool • Supports map/reduce workloads aiomultiprocess github.com/jreese/aiomultiprocess
  42. 49.
  43. 54.