• No results found

Sorting is an important step in some database operations like the sort merge join or the order by operator. Index creation almost always sorts data, except when creating hash indexes. Analysts that are interested in top-k results or different quantiles of the data also often simply sort the data set, or at least parts of it.

Sorting is a very well studied problem with well understood theoretical bounds. To further improve sorting on modern and new hardware, we tried to make better use of already existing hardware, e.g. caches and conditional loads and stores, and also give ideas on new hardware that would support faster sorting algorithms.

Small Hardware Sorts

One approach to speed up sorting operations in memory is to introduce small hardware sorts, that allow quick-sort or any partitioning sort to stop as soon as the partition size is smaller then the hardware sort width.

To measure the effectiveness of such an approach we performed a simulation. In this simulation we created a random sequence of 107 elements and sorted this

sequence using the quick-sort implementation used in the sun JDK 1.6. Whenever a partition had less then k elements we stopped the recursion and assumed a hardware sort would be performed on this partition. Figure A.6 shows the runtime

A.1. Algorithms 151 0 0.5 1 1.5 2 2.5 3 0 5 10 15 20 25 30 Time in [s] Sorting width QuickSort

Figure A.6: k wide hardware sort in Quick Sort

for different values of k. The possible gains are in the range of 10% to 30% for fixed size data types. If such gains are enough to justify investing into the development of new sorting hardware was not explored.

In-memory String Sort with Tournament Sort and UPT,CFC

Almost all business databases fit into the main memory of todays clusters and mainframes [75]. Therefore we analyze the in-memory sorting performance of two important string sorting algorithms.

Tournament Sort : External sort algorithm based on a so called loser tree, that can be found in The Art of Computer Programming Volume 3 Searching and Sorting by Donald E. Knuth [31]

Multi-key Quick-Sort: Quick-sort adaption for string sorting by Bentley and Sedgewick [15], extended to use 64 bit comparisons

Additionally we improved the Multi-key Quick-Sort algorithm to better utilize caches.

Multi-key Copy Quick-Sort: Multi-key quick-sort with improved cache local- ity by copying parts of the string

Since the Z architecture already offers some hardware assist instructions, see [51], to perform the tournament sort algorithm we compared it against multi key quick- sort. The third algorithm is an improved version of the multi-key quick-sort,

coined multi-key copy quick-sort. In the multi-key copy quick-sort we copy the currently compared part of the strings into an array and use this array to perform the comparisons. Whenever a swap operation is performed, not only the string pointer, but also the copied keys are swapped. This avoids costly cache misses when chasing the string pointers to perform comparisons.

Our sorting benchmark is based on the input generator of the Sortbenchmak website http://www.sortbenchmark.org. The input consists of ten million records of size one hundred bytes. A record in turn consists of ten random bytes, called the key, and a sequence number.

For all of the tested algorithms our benchmark starts by loading all records in main memory, this is not part of the time measurement. The next step is the run generation step. Each algorithm has an array of size slots to generate runs. In case of the tournament sort the run length is expected to be 2 · slots and for the two quick-sort variants the run length is always slots. Afterwards a merge phase is executed in which all runs are merged into a single run. If only a single run exists no merge phase is needed.

We compare the performance of the three sorting algorithms on a zGryphon+ with the performance on an Intel Xeon CPU. We can see in Figure A.7 that

0 1 2 3 4 5 6 7 24 26 28 210 212 214 216 218 220 222 224 Runtime [sec] Slots MKQuickSort TournamentSort MKCopyQuickSort

(a) Total runtime on Z

0 1 2 3 4 5 6 7 8 24 26 28 210 212 214 216 218 220 222 224 Runtime [sec] Slots MKQuickSort TournamentSort MKCopyQuickSort

(b) Total runtime on Intel

Figure A.7: Total sort times, including run generation and merging, on random data

tournament sort is the fastest, as long as we have to merge. In case of the normal multi-key qick-sort we can observe that merging helps even in memory and not only in an external sort. However, if we create a single sorted run we can see that our multi key copy quick-sort has a superior runtime. This stems from the better cache locality due to the partitioning. The curves look similar for System Z and Intel, but the tournament sort has no real advantage over quick-sort on Intel. The gap between the multi-key copy quick-sort and the other sorting approaches is also wider on Intel.

A.1. Algorithms 153 0 1 2 3 4 5 6 24 26 28 210 212 214 216 218 220 222 224 Runtime [sec] Slots MKQuickSort TournamentSort MKCopyQuickSort

(a) Total runtime with sorted input on Z 0 1 2 3 4 5 6 24 26 28 210 212 214 216 218 220 222 224 Runtime [sec] Slots MKQuickSort TournamentSort MKCopyQuickSort

(b) Total runtime with sorted input on Intel

Figure A.8: Total sort times, including run generation and merging, on sorted data

To see the effect of sortedness we also performed the sorting algorithms on already sorted data, the results can be seen in Figure A.8(a) and following. On sorted input the advantage of tournament sort is evident. If the data is fully sorted no merge phase is needed for the tournament sort. Even partially sorted input sequences drastically reduce the number of produced runs. We can clearly observe the smaller caches of the Intel CPU in Figure A.8(b).

What we can observe in our experiments is, that our improved multi-key quick- sort is the fastest for unsorted string but the tournament sort can exploit presort- edness.

Improvement of Tournament Sorting on the Mainframe

Since tournament sort is also used in IBM products, it is beneficial to improve the hardware assist instruction, to gain better performance without rewriting any client code.

Let us first have a closer look on how tournament sort works and how it is supported by the IBM System Z mainframe. In tournament sorting we sort a set of keys by creating runs and merging those runs. A run can be created by using a so called loser tree. This loser tree is a binary tree. Let us assume 2n keys fight in a tournament to find out who is the smallest. All keys are compared in the first round with their neighbor. The 2n−1 losers are stored in the leaves while

the winners proceed to the next round. This is repeated till the last two keys are compared with each other. The loser is stored in the root of the tree while the absolute winner is stored separately. If we now want to insert a new key in the tree and additionally find the next larger key then we just need to look at all keys that lost to the previous winner. In a loser tree those keys all lie on the so called winner path, i.e. the path starting with the loser of the first comparison going up till the root. Figure A.9 shows a loser tree and highlights the winner path, notice

154

45

3

65

65

46

45

46

1

23

52

42

23

52

42

3

1

Loser Tree

Winner:

Leafs:

To sort: [65 , 3 , 45 , 46 , 1 , 23 , 52 , 42 , 24 , … ]

Figure A.9: Loser tree for the sequence : [65, 3, 45, 46, 1, 23, 52, 42]

that the leafs are only virtual, that means they are not stored in memory.

We are sorting strings instead of numerical values that fit into registers. This can be solved by using codewords that fit into a register and encode a position and parts of the string key. These codewords can often be used to perform comparisons without touching the stored string. Sometimes, if two codewords are equal, the two strings need to be touched, but in total every string will be loaded at most once into CPU registers. For more details take a look in the description of the CFC (compare and form codeword) instruction in the Principles of Operation or in the COMAD paper [51].

We focus now on the update tree (UPT) instruction. The update tree instruc- tion updates a loser tree in memory by comparing code words on the winner path and swapping them if needed. This check if a swap is needed causes many branch mispredictions if the input sequence is not sorted. To avoid those expensive mis- predictions conditional loads and stores (LOC/STOC) can be used. Instead of performing speculative execution and paying the price of rolling back the instruc- tions a data stall is introduced.

The experimental evaluation of this idea can be seen in Figure A.10. The runtime of the tournament sort can be reduced by roughly 25%. We observed that the main gain was in the update tree instruction and almost no gain could be observed by introducing the conditional loads and stores in the compare and form codeword instruction.

A.1. Algorithms 155 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 24 26 28 210 212 214 216 218 220 222 224 R untime [sec] Slots Base

LOC/STOC in UPT LOC/STOC in CFC and UPTLOC/STOC in CFC

Figure A.10: Runtime of Tournament Sort with LOC/STOC

In another experiment we could also observe that on a sorted sequence the update tree instruction will perform worse with conditional loads and stores then with branches. This is obvious, since for sorted inputs there are no branch mis- predictions. In cases of low branch misprediction it is better to use branches, so it would be interesting to only switch to conditional loads and stores if a high branch misprediction rate was detected.

Another approach to speed up the update of the loser tree is to load the ele- ments of the winner path in a predefined storage area. Afterwards the swapping could be done very efficiently by a sequencer hardware similar to the sequencer used in the move character (MVC) instruction. The resulting winner path needs then to be stored back into the tree. We simulated hardware assistance for this approach and the results can be seen in Figure A.11.

The simulated sequencer approach could not beat the normal software update tree instruction in our simulations. For this we assume two main reasons. First, the winner path is not adjacent in memory and needs to be fetched from different places. This could be changed by clever prefetching, since the location of the winner path is deterministic. Second, the software implementation does not always load the whole winner path if an early stopping criterion is met. But even this could be of minor importance, since the prefetched elements of the winner path are still valid for the next update tree instruction, if it is used to sort strings. A real implementation of the sequencer idea could therefore still improve on the current UPT implementation.

                         

Figure A.11: UPT Sequencer Simulation