Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed the pivot selection in the group quick-sort #3800

Merged
merged 5 commits into from
Feb 27, 2023

Conversation

merlimat
Copy link
Contributor

Motivation

We have a custom implementation of quick-sort in the WriteCache that is used to sort an array of long that are grouped in 4: (ledgerId, entryId, offset, length).

Because of the choice of the partition pivot, the sort results in very poor performance where the items are already pre-sorted, or mostly-sorted. Similarly, it happens when they are reverse-sorted. One of the side effects are also stack-overflow when the array is big and pre-sorted.

The problem is that we're always picking the last item in the segment as the pivot, leading to always the worst case partitioning (1, N-1) items.

The simplest solution is to pick the mid item as pivot, which works well for sorted, reverse and random scenarios. Later we can try more sophisticated approaches, such as the ones from Arrays.sort().

Benchmarks

I've compared the different cases with the baseline of Arrays.sort():

Before:

Benchmark                                    Mode  Cnt       Score       Error  Units
GroupSortBenchmark.preSortedArraySort       thrpt    3  140157.757 ± 16108.997  ops/s
GroupSortBenchmark.preSortedGroupSort       thrpt    3       6.278 ±     0.149  ops/s
GroupSortBenchmark.randomArraySort          thrpt    3     647.256 ±    75.014  ops/s
GroupSortBenchmark.randomGroupSort          thrpt    3    1465.937 ±    80.175  ops/s
GroupSortBenchmark.reverseSortedArraySort   thrpt    3  138949.197 ± 10135.254  ops/s
GroupSortBenchmark.reverseSortedGroupSort   thrpt    3       6.301 ±     1.130  ops/s

After:

Benchmark                                   Mode  Cnt       Score      Error  Units
GroupSortBenchmark.preSortedArraySort      thrpt    3  138807.541 ± 3275.438  ops/s
GroupSortBenchmark.preSortedGroupSort      thrpt    3    1978.380 ±  106.142  ops/s
GroupSortBenchmark.randomArraySort         thrpt    3     673.768 ±   79.817  ops/s
GroupSortBenchmark.randomGroupSort         thrpt    3    1172.314 ±   18.191  ops/s
GroupSortBenchmark.reverseSortedArraySort  thrpt    3  141224.984 ± 2783.076  ops/s
GroupSortBenchmark.reverseSortedGroupSort  thrpt    3    1958.383 ±   73.815  ops/s

The preSortedGroupSort goes from 6.2 to 1978.3 ops/s.

@merlimat merlimat added this to the 4.16.0 milestone Feb 23, 2023
@codecov-commenter
Copy link

codecov-commenter commented Feb 23, 2023

Codecov Report

Merging #3800 (f0beb50) into master (8d92263) will increase coverage by 12.03%.
The diff coverage is 100.00%.

@@              Coverage Diff              @@
##             master    #3800       +/-   ##
=============================================
+ Coverage     56.41%   68.44%   +12.03%     
- Complexity     5531     6754     +1223     
=============================================
  Files           473      473               
  Lines         40934    40936        +2     
  Branches       5235     5235               
=============================================
+ Hits          23092    28020     +4928     
+ Misses        15744    10665     -5079     
- Partials       2098     2251      +153     
Flag Coverage Δ
bookie 39.88% <100.00%> (?)
client 44.27% <100.00%> (-0.04%) ⬇️
remaining 29.75% <0.00%> (?)
replication 41.50% <0.00%> (+0.03%) ⬆️
tls 21.05% <0.00%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
.../bookkeeper/bookie/storage/ldb/ArrayGroupSort.java 100.00% <100.00%> (+21.05%) ⬆️
.../main/java/org/apache/bookkeeper/util/ZkUtils.java 82.47% <0.00%> (-5.16%) ⬇️
...kkeeper/client/DefaultEnsemblePlacementPolicy.java 87.80% <0.00%> (-2.44%) ⬇️
...org/apache/bookkeeper/client/PendingReadLacOp.java 73.68% <0.00%> (-1.76%) ⬇️
...org/apache/bookkeeper/net/NetworkTopologyImpl.java 47.78% <0.00%> (-1.48%) ⬇️
...r/client/TopologyAwareEnsemblePlacementPolicy.java 77.68% <0.00%> (-0.28%) ⬇️
.../org/apache/bookkeeper/proto/BookieClientImpl.java 64.36% <0.00%> (ø)
.../java/org/apache/bookkeeper/client/BookKeeper.java 77.52% <0.00%> (+0.22%) ⬆️
...pache/bookkeeper/proto/PerChannelBookieClient.java 80.45% <0.00%> (+0.25%) ⬆️
...r/client/RackawareEnsemblePlacementPolicyImpl.java 82.29% <0.00%> (+0.36%) ⬆️
... and 145 more

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@hangc0276 hangc0276 requested a review from sijie February 23, 2023 01:55
@zymap
Copy link
Member

zymap commented Feb 23, 2023

Test errors:

 Failures: 
Error:  org.apache.bookkeeper.bookie.storage.ldb.ArraySortGroupTest.simple
Error:    Run 1: ArraySortGroupTest.simple:57 arrays first differed at element [2]; expected:<3> but was:<1>
Error:    Run 2: ArraySortGroupTest.simple:57 arrays first differed at element [2]; expected:<3> but was:<1>
Error:    Run 3: ArraySortGroupTest.simple:57 arrays first differed at element [2]; expected:<3> but was:<1>
[INFO] 
Error:  org.apache.bookkeeper.bookie.storage.ldb.ArraySortGroupTest.twoItems
Error:    Run 1: ArraySortGroupTest.twoItems:118 arrays first differed at element [1]; expected:<1> but was:<2>
Error:    Run 2: ArraySortGroupTest.twoItems:118 arrays first differed at element [1]; expected:<1> but was:<2>
Error:    Run 3: ArraySortGroupTest.twoItems:118 arrays first differed at element [1]; expected:<1> but was:<2>
[INFO] 
Error:  org.apache.bookkeeper.bookie.storage.ldb.DbReadLedgerIndexEntriesTest.testReadLedgerIndexEntries
Error:    Run 1: DbReadLedgerIndexEntriesTest.testReadLedgerIndexEntries:133
Error:    Run 2: DbReadLedgerIndexEntriesTest.testReadLedgerIndexEntries:133
Error:    Run 3: DbReadLedgerIndexEntriesTest.testReadLedgerIndexEntries:133
[INFO] 
Error:  org.apache.bookkeeper.bookie.storage.ldb.LedgersIndexRebuildTest.testRebuildIncludesAllLedgersAndSetToFenced
Error:    Run 1: LedgersIndexRebuildTest.testRebuildIncludesAllLedgersAndSetToFenced:118
Error:    Run 2: LedgersIndexRebuildTest.testRebuildIncludesAllLedgersAndSetToFenced:118
Error:    Run 3: LedgersIndexRebuildTest.testRebuildIncludesAllLedgersAndSetToFenced:118
[INFO] 
Error:  org.apache.bookkeeper.bookie.storage.ldb.WriteCacheTest.testLedgerDeletion
Error:    Run 1: WriteCacheTest.testLedgerDeletion:250->lambda$testLedgerDeletion$5:251 expected:<0> but was:<1>
Error:    Run 2: WriteCacheTest.testLedgerDeletion:250->lambda$testLedgerDeletion$5:251 expected:<0> but was:<1>
Error:    Run 3: WriteCacheTest.testLedgerDeletion:250->lambda$testLedgerDeletion$5:251 expected:<0> but was:<1>
[INFO] 
Error:  org.apache.bookkeeper.bookie.storage.ldb.WriteCacheTest.testMultipleWriters
Error:    Run 1: WriteCacheTest.testMultipleWriters:210->lambda$testMultipleWriters$4:211 expected:<0> but was:<1024>
Error:    Run 2: WriteCacheTest.testMultipleWriters:210->lambda$testMultipleWriters$4:211 expected:<0> but was:<1024>
Error:    Run 3: WriteCacheTest.testMultipleWriters:210->lambda$testMultipleWriters$4:211 expected:<0> but was:<1024>
[INFO] 
Error:  Errors: 
Error:  org.apache.bookkeeper.bookie.storage.ldb.ConversionRollbackTest.convertFromDbStorageToInterleaved
Error:    Run 1: ConversionRollbackTest.convertFromDbStorageToInterleaved:143 » IO Bad entry re...
Error:    Run 2: ConversionRollbackTest.convertFromDbStorageToInterleaved:143 » IO Bad entry re...
Error:    Run 3: ConversionRollbackTest.convertFromDbStorageToInterleaved:143 » IO Bad entry re...
[INFO] 
Error:  org.apache.bookkeeper.bookie.storage.ldb.DbLedgerStorageTest.testEntriesOutOfOrder
Error:    Run 1: DbLedgerStorageTest.testEntriesOutOfOrder:334 » NoEntry Entry 1 not found in 1
Error:    Run 2: DbLedgerStorageTest.testEntriesOutOfOrder:334 » NoEntry Entry 1 not found in 1
Error:    Run 3: DbLedgerStorageTest.testEntriesOutOfOrder:334 » NoEntry Entry 1 not found in 1
[INFO] 
Error:  org.apache.bookkeeper.bookie.storage.ldb.DbLedgerStorageWithDirectEntryLoggerTest.testEntriesOutOfOrder
Error:    Run 1: DbLedgerStorageWithDirectEntryLoggerTest>DbLedgerStorageTest.testEntriesOutOfOrder:334 » NoEntry
Error:    Run 2: DbLedgerStorageWithDirectEntryLoggerTest>DbLedgerStorageTest.testEntriesOutOfOrder:334 » NoEntry
Error:    Run 3: DbLedgerStorageWithDirectEntryLoggerTest>DbLedgerStorageTest.testEntriesOutOfOrder:334 » NoEntry
[INFO] 
Error:  org.apache.bookkeeper.bookie.storage.ldb.LocationsIndexRebuildTest.test
Error:    Run 1: LocationsIndexRebuildTest.test:130 » IndexOutOfBounds index: 8, length: 8 (exp...
Error:    Run 2: LocationsIndexRebuildTest.test:130 » IndexOutOfBounds index: 8, length: 8 (exp...
Error:    Run 3: LocationsIndexRebuildTest.test:130 » IndexOutOfBounds index: 8, length: 8 (exp...
[INFO] 
Error:  org.apache.bookkeeper.bookie.storage.ldb.LocationsIndexRebuildTest.testMultiLedgerIndexDiffDirs
Error:    Run 1: LocationsIndexRebuildTest.testMultiLedgerIndexDiffDirs:203 » IndexOutOfBounds ...
Error:    Run 2: LocationsIndexRebuildTest.testMultiLedgerIndexDiffDirs:203 » IndexOutOfBounds ...
Error:    Run 3: LocationsIndexRebuildTest.testMultiLedgerIndexDiffDirs:203 » IndexOutOfBounds ...
[INFO] 
Warning:  Flakes: 
Warning:  org.apache.bookkeeper.bookie.UpgradeTest.testUpgradeCurrent
Error:    Run 1: UpgradeTest.testUpgradeCurrent:228->testUpgradeProceedure:179->Object.wait:-2 » TestTimedOut
[INFO]   Run 2: PASS
[INFO] 
[INFO] 
Error:  Tests run: 658, Failures: 6, Errors: 5, Skipped: 0, Flakes: 1

@@ -61,11 +61,13 @@ private void quickSort(long[] array, int low, int high) {
}

private int partition(long[] array, int low, int high) {
int pivotIdx = high;
int mid = low + (high - low) / 2;
Copy link
Member

@horizonzy horizonzy Feb 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the mid may not be a multiple of groupSize, that will introduce incorrect swap.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch.. That's correct. I'll fix it.

Copy link
Member

@horizonzy horizonzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@merlimat merlimat merged commit 7f263fa into apache:master Feb 27, 2023
@merlimat merlimat deleted the fix-group-sort-performance branch February 27, 2023 01:00
zymap pushed a commit that referenced this pull request Dec 6, 2023
* Fixed the pivot selection in the group quick-sort

* Fixed formatting

* Fixed alignment

(cherry picked from commit 7f263fa)
Ghatage pushed a commit to sijie/bookkeeper that referenced this pull request Jul 12, 2024
* Fixed the pivot selection in the group quick-sort

* Fixed formatting

* Fixed alignment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants