v8/test/mjsunit/array-indexof-simd.js
Darius M 7c4461add3 Reland "[builtins] use SIMD IndexOf/includes on large arrays"
This is a reland of commit ab76ffc8bd.

Original change's description:
> [builtins] use SIMD IndexOf/includes on large arrays
>
> Change-Id: If751e813c7f45a4d18b84e8c0314a54c84894d61
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3639203
> Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
> Commit-Queue: Darius Mercadier <dmercadier@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#80771}

Change-Id: I81dcf3c97a15b95fd42927ff8e91602f109db315
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3672418
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Darius Mercadier <dmercadier@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80840}
2022-05-31 09:19:50 +00:00

203 lines
5.9 KiB
JavaScript

// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Large array of packed Smi, and Smi search_element
(() => {
let a = [];
for (let i = 0; i < 200; i++) {
a[i] = i;
}
function testArrayIndexOf(idx) {
return a.indexOf(idx);
}
// Without fromIndex
for (let i = 0; i < 200; i++) {
assertEquals(i, testArrayIndexOf(i));
}
// With fromIndex
for (let i = 0, from_index = 0; i+from_index < 200; i += 2, from_index++) {
assertEquals(i, testArrayIndexOf(i, from_index));
}
})();
// Large array of holey Smi, and Smi search_element
(() => {
let a = [];
// Skipping every other item when initializing
for (let i = 0; i < 200; i+=2) {
a[i] = i;
}
function testArrayIndexOf(idx) {
return a.indexOf(idx);
}
// Without fromIndex
for (let i = 0; i < 200; i++) {
if (i % 2 == 0) {
assertEquals(i, testArrayIndexOf(i));
} else {
assertEquals(-1, testArrayIndexOf(i));
}
}
// With fromIndex
for (let i = 0, from_index = 0; i + from_index < 200; i += 2, from_index++) {
if (i % 2 == 0) {
assertEquals(i, testArrayIndexOf(i, from_index));
} else {
assertEquals(-1, testArrayIndexOf(i, from_index));
}
}
})();
// Large array of packed Doubles, and Double search_element
(() => {
let a = [];
for (let i = 0; i < 200; i++) {
a[i] = i + 0.5;
}
function testArrayIndexOf(idx) {
return a.indexOf(idx);
}
// Without fromIndex
for (let i = 0; i < 200; i++) {
assertEquals(i, testArrayIndexOf(i + 0.5));
}
// With fromIndex
for (let i = 0, from_index = 0; i+from_index < 200; i += 2, from_index++) {
assertEquals(i, testArrayIndexOf(i+0.5, from_index));
}
})();
// Large array of holey Doubles, and Double search_element
(() => {
let a = [];
// Skipping every other item when initializing
for (let i = 0; i < 200; i+=2) {
a[i] = i + 0.5;
}
function testArrayIndexOf(idx) {
return a.indexOf(idx);
}
// Without fromIndex
for (let i = 0; i < 200; i++) {
if (i % 2 == 0) {
assertEquals(i, testArrayIndexOf(i + 0.5));
} else {
assertEquals(-1, testArrayIndexOf(i + 0.5));
}
}
// With fromIndex
for (let i = 0, from_index = 0; i + from_index < 200; i += 2, from_index++) {
if (i % 2 == 0) {
assertEquals(i, testArrayIndexOf(i+0.5, from_index));
} else {
assertEquals(-1, testArrayIndexOf(i+0.5, from_index));
}
}
})();
// Large array of packed objects, and object search_element
(() => {
let a = [];
let b = [];
for (let i = 0; i < 200; i++) {
a[i] = { v: i };
b[i] = a[i];
}
function testArrayIndexOf(idx) {
return a.indexOf(idx);
}
// Without fromIndex
for (let i = 0; i < 200; i++) {
assertEquals(i, testArrayIndexOf(b[i]));
}
// With fromIndex
for (let i = 0, from_index = 0; i+from_index < 200; i += 2, from_index++) {
assertEquals(i, testArrayIndexOf(b[i], from_index));
}
})();
// Large array of holey objects, and object search_element
(() => {
let a = [];
let b = [];
// Skipping every other item when initializing
for (let i = 0; i < 200; i++) {
b[i] = { v: i };
if (i % 2 == 0) {
a[i] = b[i];
}
}
function testArrayIndexOf(idx) {
return a.indexOf(idx);
}
// Without fromIndex
for (let i = 0; i < 200; i++) {
if (i % 2 == 0) {
assertEquals(i, testArrayIndexOf(b[i]));
} else {
assertEquals(-1, testArrayIndexOf(b[i]));
}
}
// With fromIndex
for (let i = 0, from_index = 0; i + from_index < 200; i += 2, from_index++) {
if (i % 2 == 0) {
assertEquals(i, testArrayIndexOf(b[i], from_index));
} else {
assertEquals(-1, testArrayIndexOf(b[i], from_index));
}
}
})();
// This test checks that when the item that IndexOf searches is present multiple
// time, the correct index is returned (in particular, when a single SIMD vector
// had multiple matches). For instance, if we do:
//
// [1, 2, 1, 3].indexOf(1)
//
// Then it should return 0 rather than 2.
(() => {
// The patterns that this function will check, where for instance patternABAB
// means that we'd like to build a vector containing {A, B, A, B}.
let patterns = {
patternABAB : (a, b, c, d) => [a, b, a, b, c, d, c, d],
patternAABB : (a, b, c, d) => [a, a, b, b, c, c, d, d],
patternABBA : (a, b, c, d) => [a, b, b, a, c, d, d, c],
patternABAA : (a, b, c, d) => [a, b, a, a, c, d, c, c],
patternAABA : (a, b, c, d) => [a, a, b, a, c, c, d, c],
patternAAAB : (a, b, c, d) => [a, a, a, b, c, c, c, d],
patternBAAA : (a, b, c, d) => [b, a, a, a, d, c, c, c]
};
// Starting |a| with a bunch of 0s, which might be handled by the scalar loop
// that the SIMD code does to reach 16/32-byte alignment.
let a = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let next_int = 1;
for (const [_, pattern] of Object.entries(patterns)) {
// It's a bit tricky to ensure that 2 items will be in the same SIMD batch
// because we can't control the alignment of the array from JS, and the
// SIMD code will start by skipping the first items to have the memory
// aligned on 16/32 bytes. So, we put each pattern 8 times in a row in |a|,
// but each time with an additional item, to make sure that each of those 8
// repeated pattern have a different alignment.
for (let i = 0; i < 8; i++) {
a = a.concat(pattern(next_int, next_int + 1, next_int + 2, next_int + 3));
a.push(next_int + 4); // By adding a 9th item, we make sure that the
// alignment of the next batch is not the same as
// the current one.
next_int += 5;
}
}
let b = a.slice();
b[10000] = 42; // Switch b to dictionary mode so that the SIMD code won't be
// used for it. We can then use `b.indexOf` as reference.
for (let x of b) {
if (x == undefined) break;
assertEquals(b.indexOf(x), a.indexOf(x));
}
})();