Optimizing the performance of array.prototype.sort.

Signed-off-by: liujia178 <liujia178@huawei.com>
This commit is contained in:
liujia178 2024-04-09 20:41:02 +08:00
parent 43d0fa3d5a
commit 4a929ced39
4 changed files with 103 additions and 2 deletions

View File

@ -498,10 +498,87 @@ void JSArray::SortElements(JSThread *thread, const JSHandle<TaggedArray> &elemen
{
ASSERT(fn->IsUndefined() || fn->IsCallable());
uint32_t len = elements->GetLength();
// 64: if the elements is more than 64, use merge-sort algorithm.
if (len < 64) {
SortElementsByInsertionSort(thread, elements, len, fn);
} else {
SortElementsByMergeSort(thread, elements, fn, 0, len - 1);
}
}
void JSArray::SortElementsByMergeSort(JSThread *thread, const JSHandle<TaggedArray> &elements,
const JSHandle<JSTaggedValue> &fn, int64_t startIdx, int64_t endIdx)
{
if (startIdx >= endIdx)
return;
int64_t middleIdx = startIdx + (endIdx - startIdx) / 2; // 2: half
SortElementsByMergeSort(thread, elements, fn, startIdx, middleIdx);
SortElementsByMergeSort(thread, elements, fn, middleIdx + 1, endIdx);
MergeSortedElements(thread, elements, fn, startIdx, middleIdx, endIdx);
}
void JSArray::MergeSortedElements(JSThread *thread, const JSHandle<TaggedArray> &elements,
const JSHandle<JSTaggedValue> &fn, int64_t startIdx,
int64_t middleIdx, int64_t endIdx)
{
int64_t leftLength = middleIdx - startIdx + 1;
int64_t rightLength = endIdx - middleIdx;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> leftArray = factory->NewTaggedArray(leftLength);
JSHandle<TaggedArray> rightArray = factory->NewTaggedArray(rightLength);
for (int64_t i = 0; i < leftLength; i++) {
leftArray->Set(thread, i, elements->Get(startIdx + i));
}
for (int64_t j = 0; j < rightLength; j++) {
rightArray->Set(thread, j, elements->Get(middleIdx + 1 + j));
}
int64_t i = 0;
int64_t j = 0;
int64_t k = startIdx;
JSMutableHandle<JSTaggedValue> leftValue(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> rightValue(thread, JSTaggedValue::Undefined());
while (i < leftLength && j < rightLength) {
leftValue.Update(leftArray->Get(i));
rightValue.Update(rightArray->Get(j));
int64_t compareRet = base::ArrayHelper::SortCompare(thread, fn, leftValue, rightValue);
RETURN_IF_ABRUPT_COMPLETION(thread);
if (compareRet <= 0) {
elements->Set(thread, k, leftArray->Get(i));
i++;
} else {
elements->Set(thread, k, rightArray->Get(j));
j++;
}
k++;
}
while (i < leftLength) {
elements->Set(thread, k, leftArray->Get(i));
i++;
k++;
}
while (j < rightLength) {
elements->Set(thread, k, rightArray->Get(j));
j++;
k++;
}
}
void JSArray::SortElementsByInsertionSort(JSThread *thread, const JSHandle<TaggedArray> &elements, uint32_t len,
const JSHandle<JSTaggedValue> &fn)
{
if (len <= 1)
return;
JSMutableHandle<JSTaggedValue> presentValue(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> middleValue(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> previousValue(thread, JSTaggedValue::Undefined());
uint32_t len = elements->GetLength();
for (uint32_t i = 1; i < len; i++) {
uint32_t beginIndex = 0;
uint32_t endIndex = i;

View File

@ -112,7 +112,13 @@ public:
const JSHandle<JSTaggedValue> &fn);
static void SortElementsByObject(JSThread *thread, const JSHandle<JSObject> &thisObjHandle,
const JSHandle<JSTaggedValue> &fn);
static void SortElementsByInsertionSort(JSThread *thread, const JSHandle<TaggedArray> &elements, uint32_t len,
const JSHandle<JSTaggedValue> &fn);
static void SortElementsByMergeSort(JSThread *thread, const JSHandle<TaggedArray> &elements,
const JSHandle<JSTaggedValue> &fn, int64_t startIdx, int64_t endIdx);
static void MergeSortedElements(JSThread *thread, const JSHandle<TaggedArray> &elements,
const JSHandle<JSTaggedValue> &fn, int64_t startIdx, int64_t middleIdx, int64_t endIdx);
template <class Callback>
static JSTaggedValue ArrayCreateWithInit(JSThread *thread, uint32_t length, const Callback &cb)
{

View File

@ -68,6 +68,23 @@ arr1.sort((a, b) => {
});
print(JSON.stringify(arr1));
/*
* Test Case Description:
* 1. This use case is used to verify the logical processing order of the quick sorting algorithm.
* 2. If there are any changes to the use case, please confirm if the use case needs to be modified.
*/
for (let i = 0; i < 100; i++) {
arr1[i] = i;
}
arr1[0] = 99;
arr1[99] = 0;
arr1[49] = 50;
arr1[50] = 49;
arr1.sort((a, b) => {
return a - b;
})
print(JSON.stringify(arr1));
// Modification of objects during the comparison process.
let arr2 = [1, 3, 2];
arr2.sort((a, b) => {

View File

@ -20,6 +20,7 @@ comparing a = 1, b = 3
comparing a = 3, b = 2
comparing a = 1, b = 2
[1,2,3]
[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99]
[1,2,3]
[1,2,3]
{"0":1,"1":2,"2":3,"a":6,"length":3}