timeout_heap.txt 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. How the timeout heap works
  2. As of version 1.5, the State Threads Library represents the queue of
  3. sleeping threads using a heap data structure rather than a sorted
  4. linked list. This improves performance when there is a large number
  5. of sleeping threads, since insertion into a heap takes O(log N) time
  6. while insertion into a sorted list takes O(N) time. For example, in
  7. one test 1000 threads were created, each thread called st_usleep()
  8. with a random time interval, and then all the threads where
  9. immediately interrupted and joined before the sleeps had a chance to
  10. finish. The whole process was repeated 1000 times, for a total of a
  11. million sleep queue insertions and removals. With the old list-based
  12. sleep queue, this test took 100 seconds; now it takes only 12 seconds.
  13. Heap data structures are typically based on dynamically resized
  14. arrays. However, since the existing ST code base was very nicely
  15. structured around linking the thread objects into pointer-based lists
  16. without the need for any auxiliary data structures, implementing the
  17. heap using a similar nodes-and-pointers based approach seemed more
  18. appropriate for ST than introducing a separate array.
  19. Thus, the new ST timeout heap works by organizing the existing
  20. _st_thread_t objects in a balanced binary tree, just as they were
  21. previously organized into a doubly-linked, sorted list. The global
  22. _ST_SLEEPQ variable, formerly a linked list head, is now simply a
  23. pointer to the root of this tree, and the root node of the tree is the
  24. thread with the earliest timeout. Each thread object has two child
  25. pointers, "left" and "right", pointing to threads with later timeouts.
  26. Each node in the tree is numbered with an integer index, corresponding
  27. to the array index in an array-based heap, and the tree is kept fully
  28. balanced and left-adjusted at all times. In other words, the tree
  29. consists of any number of fully populated top levels, followed by a
  30. single bottom level which may be partially populated, such that any
  31. existing nodes form a contiguous block to the left and the spaces for
  32. missing nodes form a contiguous block to the right. For example, if
  33. there are nine threads waiting for a timeout, they are numbered and
  34. arranged in a tree exactly as follows:
  35. 1
  36. / \
  37. 2 3
  38. / \ / \
  39. 4 5 6 7
  40. / \
  41. 8 9
  42. Each node has either no children, only a left child, or both a left
  43. and a right child. Children always time out later than their parents
  44. (this is called the "heap invariant"), but when a node has two
  45. children, their mutual order is unspecified - the left child may time
  46. out before or after the right child. If a node is numbered N, its
  47. left child is numbered 2N, and its right child is numbered 2N+1.
  48. There is no pointer from a child to its parent; all pointers point
  49. downward. Additions and deletions both work by starting at the root
  50. and traversing the tree towards the leaves, going left or right
  51. according to the binary digits forming the index of the destination
  52. node. As nodes are added or deleted, existing nodes are rearranged to
  53. maintain the heap invariant.