Skip to content

8360023: Add an insertion sort implementation to Hotspot #25895

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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions src/hotspot/share/utilities/sort.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_UTILITIES_SORT_HPP
#define SHARE_UTILITIES_SORT_HPP

#include "memory/allStatic.hpp"

// An insertion sort that is stable and inplace.
// This algorithm should be the ideal solution to sort a sequence with few elements. Arrays::sort
// uses insertion sort for arrays up to around 50 elements.
// comp should return a value > 0 iff the first argument is larger than the second argument. A full
// comparison function satisfies this requirement but a simple a > b ? 1 : 0 also satisfies it.
class InsertionSort : AllStatic {
public:
template <class T, class Compare>
static void sort(T* data, int size, Compare comp) {
Copy link
Contributor

Choose a reason for hiding this comment

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

int size to size_t size ? or at least unsigned int.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hotspot container usually uses signed int for size. So I think int here is a sensible choice.

if (size == 0) {
return;
}

T* begin = data;
T* end = data + size;
for (T* current = begin + 1; current < end; current++) {
T current_elem = *current;

// Elements in [begin, current) has already been sorted, we search backward to find a
// location to insert the element at current. In the meantime, shift all elements on the way
// up by 1.
T* pos = current;
while (pos > begin) {
// Because the sort is stable, we must insert the current element at the first location at
// which the element is not greater than the current element (note that we are traversing
// backward)
T* prev = pos - 1;
if (comp(*prev, current_elem) <= 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: would be better to pass pointers here?

Copy link
Member Author

Choose a reason for hiding this comment

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

A comp usually receives references. Practically, it is almost the same as receiving pointers.

Copy link
Contributor

Choose a reason for hiding this comment

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

Apologies, I didn't notice it was a reference.

break;
}

*pos = *prev;
pos = prev;
}

// Move current_elem to pos since all elements in [pos, current) have been shifted up by 1
if (pos < current) {
*pos = current_elem;
}
}
}
};

#endif // SHARE_UTILITIES_SORT_HPP
94 changes: 94 additions & 0 deletions test/hotspot/gtest/utilities/test_sort.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#include "runtime/os.hpp"
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: sort the imports?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have cleaned up the unused import here. What do you mean by sorting the imports?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sort the "#include" lines alphabetically.

Copy link
Member Author

Choose a reason for hiding this comment

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

I assume you want to have unittest.hpp above the utilities files. I have done that. I was confused because the convention in this area is pretty blurry as many files have the unittest.hpp as their last include.

#include "unittest.hpp"
#include "utilities/powerOfTwo.hpp"
#include "utilities/sort.hpp"

constexpr int TEST_ARRAY_SIZE = 128;

class TwoInt {
public:
int val;
int idx;

TwoInt() : val(0), idx(0) {}
TwoInt(int val, int idx) : val(val), idx(idx) {}
};

int ARRAY0[TEST_ARRAY_SIZE];
TwoInt ARRAY1[TEST_ARRAY_SIZE];

// Verify that the sort is correct, i.e. a[i] <= a[i + 1]
void test_insertion_sort(int size) {
assert(size <= TEST_ARRAY_SIZE, "invalid parameter");
for (int i = 0; i < size; i++) {
ARRAY0[i] = os::random();
}
InsertionSort::sort(ARRAY0, size, [](int a, int b) {
return a > b ? 1 : 0;
});
for (int i = 0; i < size - 1; i++) {
ASSERT_TRUE(ARRAY0[i] <= ARRAY0[i + 1]);
}
}

// Verify that the sort is stable. Since there are 128 elements but the keys can only take 16
// values, there will inevitably be a lot of elements with the same key. We then verify that if the
// keys of 2 elements are the same, then the element that has the smaller idx will be ordered
// before the one with the larger idx.
void test_insertion_sort_stable(int size, int key_bound) {
assert(size <= TEST_ARRAY_SIZE, "invalid parameter");
assert(is_power_of_2(key_bound), "invalid parameter");
for (int i = 0; i < size; i++) {
ARRAY1[i] = TwoInt(os::random() & (key_bound - 1), i);
}
InsertionSort::sort(ARRAY1, size, [](TwoInt a, TwoInt b) {
return a.val > b.val ? 1 : 0;
});
for (int i = 0; i < size - 1; i++) {
TwoInt a = ARRAY1[i];
TwoInt b = ARRAY1[i + 1];
ASSERT_TRUE(a.val <= b.val);
if (a.val == b.val) {
ASSERT_TRUE(a.idx < b.idx);
}
}
}

TEST(utilities, insertion_sort) {
for (int i = 0; i < 100; i++) {
test_insertion_sort(0);
test_insertion_sort(1);
test_insertion_sort(2);
test_insertion_sort(10);
test_insertion_sort(TEST_ARRAY_SIZE);
test_insertion_sort_stable(1, 1);
test_insertion_sort_stable(2, 1);
test_insertion_sort_stable(3, 2);
test_insertion_sort_stable(10, 4);
test_insertion_sort_stable(TEST_ARRAY_SIZE, 16);
}
}