At the onset, the front-end structure and the Twol back-end (both 1 and 2) are empty. All Insert ( , ) operations insert events into 2, i.e. Insert( , 2), without a linear search for the correct event position. Events are simply appended to the
unsorted linked list ¡2 . On the first DeleteMin ( )¢ , all the events in ¡2 are transferred to ¡1. The bucketwidth of ¡1 is determined using the expression:
Bucketwidth of Ă1 = Ă1_ÊÔ = (Ă2_Ơ ƯĐ –Ă2_Ơăâ) / Ă2_êôơ, (2.1) and the bucket index in which an event is inserted in ¡1 is determined similarly to SCQ:
Bucket index = ( ( ) ® −¡1_¢¦¯) /¡1_£ ¤. (2.2) The rationale of using Equation (2.1) to obtain the bucketwidth is that Twol makes an assumption that the inter-event timestamp is uniformly distributed. Hence each bucket should contain one event on average if the distribution is uniform. If the assumption of uniform distribution is not true, there would exist some buckets that contain many events. However the performance is not affected even if some buckets contain many events. This is because subsequent insertion cost of events into ¡1, or Insert( , )®¡1 , is still °(1) since events are simply appended to the sublists without sorting. After obtaining Ă1_ÊÔ, both Ă1_ÂưƯ¯ ư and Ă1_±ô¯ are set to Ă2²Ơăâ, and
2
¡ _¢¦¯ is set to ¡2_¥ ¦§. The bucket index of all the events inserted into ¡1 during a transfer from ¡2 to ¡1 adhere to Equation (2.2), which is similar to an event inserted in an SCQ but without requiring modulus by the number of buckets, unlike in an SCQ. This is different from an SCQ, because in an SCQ there is no dedicated overflow structure but rather the overflow events are inserted into the sublists, forming multiple ³®¦¯´, where a year is the time interval that spans the total length of all the bucket sublists. For ¡1, there exists only one year.
Thereafter, the buckets in ¡1 are processed on a bucket-by-bucket basis, starting with the first non-empty bucket of ¡1. If there is only one event in a bucket,
the event is simply deleted since it is the event with the minimum timestamp, thereby bypassing the front-end structure. If however there are more than one event in a bucket, the events are to be inserted into the front-end structure using the front- end priority queue’s native Insert operation. And thereafter, the front-end priority queue’s native DeleteMin operation is carried out. After the events in the front-end are depleted, the second non-empty bucket in ả1 is processed and so on, until both the front-end structure and ả1 are empty. Under that circumstance, a transfer of events from ả2 to ả1 is performed again. We shall illustrate the DeleteMin ( )ã of the Twol algorithm in the following example.
Let
á
ạ be the number of events in the front-end structure, 1
º
ạ and 2
º
ạ be the number of events in ả1 and ả2 of Twol respectively,
º ằẳẵ
ạ be the number of events in the Twol back-end = 1 2
º º
ạ +ạ and the total number of events
á º ằẳẵ
ạ=ạ +ạ . As an example, assume ạ=0 initially. Insert eight events which have timestamps 36, 10, 51, 100, 25, 175, 226, 127 according to this order. These eight events will be appended to the head of ả2 without sorting and the variables of
2
ả will be updated as: ả2_ắ¿ạ = 10, ả2ÀắÁÂ = 226, ả2_ÃÄÅ = 8, ã = {127, 226, 175, 25, 100, 51, 10, 36}, where event with timestamp 127 is now at the head of ả2. On the first DeleteMin ( )ã operation, because
á
ạ and 1
º
ạ = 0, a transfer of events from ả2 to ả1 is necessary. ả1’s variables are initialized: ả1_ãặÁầặ and ả1_ẩÄầ = 10, and from Equation (2.1), ả1_ẫ ấ = (226 – 10) / 8 = 27. The events are then inserted in the corresponding bucket using Equation (2.2). For instance, the event with timestamp 36 is inserted into the bucket index = (36 10) / 27 − = 0.
Table 2-2: Arrangement of Events in Ë1 of Twol Bucket
Index 0 1 2 3 4 5 6 7 8
36 51 – 100 127 – 175 – 226
10 25
After the transfer, the events in Ë1 will be arranged as seen in Table 2-2.
Also, ậ2_èÍẻ is then tuned to be ậ2_ẽ éẹ (i.e. 226) so that additional events to be inserted and which timestamps are greater or equal to ậ2_èÍẻ (i.e. 226), will fall in
2
Ë and not in Ë1 nor the front-end structure. Finally, the events {36, 10, 25} are then transferred from the first non-empty bucket, Bucket[0], to the front-end structure in a stable FIFO manner. ậ1_èÍẻ is then set to ậ1_ềểéẻể+ậ1_ễ ế = 37. Thereafter the event timestamp 10 is then subsequently deleted using the front-end structure’s DeleteMin ( )
ệ
Ò , where
ệ
Ò is the set of events in the front-end structure = {10, 25, 36}.
After events with timestamps 25 and 36 are deleted and assuming there is no additional Insert operation, the subsequent DeleteMin operation will then process the next non-empty bucket, Bucket[1], which contains one event with timestamp 51.
Since there is only one event, the DeleteMin operation will simply delete this event to be processed, bypassing the front-end structure for more efficiency. That is, the greater the number of buckets that contain only one event, the more efficient is the Twol algorithm.
2.4.1 Successive DeleteMin Operations Creates Epochs
It should be noted that the Twol back-end operates in epochs when successive DeleteMin operations take place. The start of an epoch is marked by the creation of
1
× and the front-end structure. This occurs when all the events are stored in ×2 (there is currently no ×1 nor front-end structure) and the first DeleteMin operation is initiated (which creates both ×1 and a front-end structure). The parameters of ×1 is unique for each epoch and are determined using Equations (2.1) and (2.2). After the epoch is started, subsequent Insert operations can result in events being inserted into either ×1, ×2 or the front-end structure.
In a stable queue system where the number of events inserted into the system is relatively equal to the number of events being deleted, each DeleteMin operation will advance the current timestamp, on average, by the mean of the jump parameter (à) of the event distribution. This means that as the timestamp advances, more and more buckets in ×1 will be dequeued of events and become empty while ×2 will gradually grow in size. Since ×1 has a finite number of buckets, there must exist a time when ×1 and the front-end structure becomes emptied of events and all future events are now found only in ×2. When this occurs, i.e. ×1 and the front-end structure are empty, this marks the end of the epoch.
The next epoch begins when Equations (2.1) and (2.2) are employed again to derive the new operating parameters of ×1. It should be noted that for the new epoch, the value of the variables used in the equations are associated with the current future events stored in ×2. This means that the Twol back-end will always operate with a
new set of operating parameters tailored for events in the new epoch. This is clearly desirable.