8320448: Accelerate IndexOf using AVX2

Reviewed-by: epeter, kvn, sviswanathan
This commit is contained in:
Scott Gibbons 2024-06-07 17:02:14 +00:00 committed by Jatin Bhateja
parent 25ad86234a
commit 8e72d7cf8e
16 changed files with 3906 additions and 30 deletions

View File

@ -4491,13 +4491,21 @@ void C2_MacroAssembler::count_positives(Register ary1, Register len,
// Compare char[] or byte[] arrays aligned to 4 bytes or substrings.
void C2_MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register ary2,
Register limit, Register result, Register chr,
XMMRegister vec1, XMMRegister vec2, bool is_char, KRegister mask) {
XMMRegister vec1, XMMRegister vec2, bool is_char,
KRegister mask, bool expand_ary2) {
// for expand_ary2, limit is the (smaller) size of the second array.
ShortBranchVerifier sbv(this);
Label TRUE_LABEL, FALSE_LABEL, DONE, COMPARE_VECTORS, COMPARE_CHAR, COMPARE_BYTE;
assert((!expand_ary2) || ((expand_ary2) && (UseAVX == 2)),
"Expansion only implemented for AVX2");
int length_offset = arrayOopDesc::length_offset_in_bytes();
int base_offset = arrayOopDesc::base_offset_in_bytes(is_char ? T_CHAR : T_BYTE);
Address::ScaleFactor scaleFactor = expand_ary2 ? Address::times_2 : Address::times_1;
int scaleIncr = expand_ary2 ? 8 : 16;
if (is_array_equ) {
// Check the input args
cmpoop(ary1, ary2);
@ -4533,14 +4541,20 @@ void C2_MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register
if (UseAVX >= 2) {
// With AVX2, use 32-byte vector compare
Label COMPARE_WIDE_VECTORS, COMPARE_TAIL;
Label COMPARE_WIDE_VECTORS, COMPARE_WIDE_VECTORS_16, COMPARE_TAIL, COMPARE_TAIL_16;
// Compare 32-byte vectors
andl(result, 0x0000001f); // tail count (in bytes)
andl(limit, 0xffffffe0); // vector count (in bytes)
jcc(Assembler::zero, COMPARE_TAIL);
if (expand_ary2) {
andl(result, 0x0000000f); // tail count (in bytes)
andl(limit, 0xfffffff0); // vector count (in bytes)
jcc(Assembler::zero, COMPARE_TAIL);
} else {
andl(result, 0x0000001f); // tail count (in bytes)
andl(limit, 0xffffffe0); // vector count (in bytes)
jcc(Assembler::zero, COMPARE_TAIL_16);
}
lea(ary1, Address(ary1, limit, Address::times_1));
lea(ary1, Address(ary1, limit, scaleFactor));
lea(ary2, Address(ary2, limit, Address::times_1));
negptr(limit);
@ -4583,25 +4597,59 @@ void C2_MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register
}//if (VM_Version::supports_avx512vlbw())
#endif //_LP64
bind(COMPARE_WIDE_VECTORS);
vmovdqu(vec1, Address(ary1, limit, Address::times_1));
vmovdqu(vec2, Address(ary2, limit, Address::times_1));
vmovdqu(vec1, Address(ary1, limit, scaleFactor));
if (expand_ary2) {
vpmovzxbw(vec2, Address(ary2, limit, Address::times_1), Assembler::AVX_256bit);
} else {
vmovdqu(vec2, Address(ary2, limit, Address::times_1));
}
vpxor(vec1, vec2);
vptest(vec1, vec1);
jcc(Assembler::notZero, FALSE_LABEL);
addptr(limit, 32);
addptr(limit, scaleIncr * 2);
jcc(Assembler::notZero, COMPARE_WIDE_VECTORS);
testl(result, result);
jcc(Assembler::zero, TRUE_LABEL);
vmovdqu(vec1, Address(ary1, result, Address::times_1, -32));
vmovdqu(vec2, Address(ary2, result, Address::times_1, -32));
vmovdqu(vec1, Address(ary1, result, scaleFactor, -32));
if (expand_ary2) {
vpmovzxbw(vec2, Address(ary2, result, Address::times_1, -16), Assembler::AVX_256bit);
} else {
vmovdqu(vec2, Address(ary2, result, Address::times_1, -32));
}
vpxor(vec1, vec2);
vptest(vec1, vec1);
jccb(Assembler::notZero, FALSE_LABEL);
jmpb(TRUE_LABEL);
jcc(Assembler::notZero, FALSE_LABEL);
jmp(TRUE_LABEL);
bind(COMPARE_TAIL_16); // limit is zero
movl(limit, result);
// Compare 16-byte chunks
andl(result, 0x0000000f); // tail count (in bytes)
andl(limit, 0xfffffff0); // vector count (in bytes)
jcc(Assembler::zero, COMPARE_TAIL);
lea(ary1, Address(ary1, limit, scaleFactor));
lea(ary2, Address(ary2, limit, Address::times_1));
negptr(limit);
bind(COMPARE_WIDE_VECTORS_16);
movdqu(vec1, Address(ary1, limit, scaleFactor));
if (expand_ary2) {
vpmovzxbw(vec2, Address(ary2, limit, Address::times_1), Assembler::AVX_128bit);
} else {
movdqu(vec2, Address(ary2, limit, Address::times_1));
}
pxor(vec1, vec2);
ptest(vec1, vec1);
jcc(Assembler::notZero, FALSE_LABEL);
addptr(limit, scaleIncr);
jcc(Assembler::notZero, COMPARE_WIDE_VECTORS_16);
bind(COMPARE_TAIL); // limit is zero
movl(limit, result);
@ -4646,19 +4694,34 @@ void C2_MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register
}
// Compare 4-byte vectors
andl(limit, 0xfffffffc); // vector count (in bytes)
jccb(Assembler::zero, COMPARE_CHAR);
if (expand_ary2) {
testl(result, result);
jccb(Assembler::zero, TRUE_LABEL);
} else {
andl(limit, 0xfffffffc); // vector count (in bytes)
jccb(Assembler::zero, COMPARE_CHAR);
}
lea(ary1, Address(ary1, limit, Address::times_1));
lea(ary1, Address(ary1, limit, scaleFactor));
lea(ary2, Address(ary2, limit, Address::times_1));
negptr(limit);
bind(COMPARE_VECTORS);
movl(chr, Address(ary1, limit, Address::times_1));
cmpl(chr, Address(ary2, limit, Address::times_1));
jccb(Assembler::notEqual, FALSE_LABEL);
addptr(limit, 4);
jcc(Assembler::notZero, COMPARE_VECTORS);
if (expand_ary2) {
// There are no "vector" operations for bytes to shorts
movzbl(chr, Address(ary2, limit, Address::times_1));
cmpw(Address(ary1, limit, Address::times_2), chr);
jccb(Assembler::notEqual, FALSE_LABEL);
addptr(limit, 1);
jcc(Assembler::notZero, COMPARE_VECTORS);
jmp(TRUE_LABEL);
} else {
movl(chr, Address(ary1, limit, Address::times_1));
cmpl(chr, Address(ary2, limit, Address::times_1));
jccb(Assembler::notEqual, FALSE_LABEL);
addptr(limit, 4);
jcc(Assembler::notZero, COMPARE_VECTORS);
}
// Compare trailing char (final 2 bytes), if any
bind(COMPARE_CHAR);

View File

@ -289,10 +289,11 @@ public:
void count_positives(Register ary1, Register len,
Register result, Register tmp1,
XMMRegister vec1, XMMRegister vec2, KRegister mask1 = knoreg, KRegister mask2 = knoreg);
// Compare char[] or byte[] arrays.
void arrays_equals(bool is_array_equ, Register ary1, Register ary2,
Register limit, Register result, Register chr,
XMMRegister vec1, XMMRegister vec2, bool is_char, KRegister mask = knoreg);
void arrays_equals(bool is_array_equ, Register ary1, Register ary2, Register limit,
Register result, Register chr, XMMRegister vec1, XMMRegister vec2,
bool is_char, KRegister mask = knoreg, bool expand_ary2 = false);
void arrays_hashcode(Register str1, Register cnt1, Register result,
Register tmp1, Register tmp2, Register tmp3, XMMRegister vnext,

File diff suppressed because it is too large Load Diff

View File

@ -4241,6 +4241,12 @@ void StubGenerator::generate_compiler_stubs() {
generate_chacha_stubs();
#ifdef COMPILER2
if ((UseAVX == 2) && EnableX86ECoreOpts) {
generate_string_indexof(StubRoutines::_string_indexof_array);
}
#endif
if (UseAdler32Intrinsics) {
StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32();
}

View File

@ -575,6 +575,9 @@ class StubGenerator: public StubCodeGenerator {
void generate_libm_stubs();
#ifdef COMPILER2
void generate_string_indexof(address *fnptrs);
#endif
address generate_cont_thaw(const char* label, Continuation::thaw_kind kind);
address generate_cont_thaw();

View File

@ -37,7 +37,7 @@ enum platform_dependent_constants {
_continuation_stubs_code_size = 1000 LP64_ONLY(+1000),
// AVX512 intrinsics add more code in 64-bit VM,
// Windows have more code to save/restore registers
_compiler_stubs_code_size = 20000 LP64_ONLY(+39000) WINDOWS_ONLY(+2000),
_compiler_stubs_code_size = 20000 LP64_ONLY(+46000) WINDOWS_ONLY(+2000),
_final_stubs_code_size = 10000 LP64_ONLY(+20000) WINDOWS_ONLY(+2000) ZGC_ONLY(+20000)
};

View File

@ -2197,6 +2197,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call) {
strcmp(call->as_CallLeaf()->_name, "bigIntegerRightShiftWorker") == 0 ||
strcmp(call->as_CallLeaf()->_name, "bigIntegerLeftShiftWorker") == 0 ||
strcmp(call->as_CallLeaf()->_name, "vectorizedMismatch") == 0 ||
strcmp(call->as_CallLeaf()->_name, "stringIndexOf") == 0 ||
strcmp(call->as_CallLeaf()->_name, "arraysort_stub") == 0 ||
strcmp(call->as_CallLeaf()->_name, "array_partition_stub") == 0 ||
strcmp(call->as_CallLeaf()->_name, "get_class_id_intrinsic") == 0 ||

View File

@ -1205,6 +1205,9 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) {
Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE);
Node* tgt_count = load_array_length(tgt);
Node* result = nullptr;
bool call_opt_stub = (StubRoutines::_string_indexof_array[ae] != nullptr);
if (ae == StrIntrinsicNode::UU || ae == StrIntrinsicNode::UL) {
// Divide src size by 2 if String is UTF16 encoded
src_count = _gvn.transform(new RShiftINode(src_count, intcon(1)));
@ -1214,7 +1217,16 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) {
tgt_count = _gvn.transform(new RShiftINode(tgt_count, intcon(1)));
}
Node* result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count, result_rgn, result_phi, ae);
if (call_opt_stub) {
Node* call = make_runtime_call(RC_LEAF, OptoRuntime::string_IndexOf_Type(),
StubRoutines::_string_indexof_array[ae],
"stringIndexOf", TypePtr::BOTTOM, src_start,
src_count, tgt_start, tgt_count);
result = _gvn.transform(new ProjNode(call, TypeFunc::Parms));
} else {
result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count,
result_rgn, result_phi, ae);
}
if (result != nullptr) {
result_phi->init_req(3, result);
result_rgn->init_req(3, control());
@ -1226,7 +1238,7 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) {
return true;
}
//-----------------------------inline_string_indexOf-----------------------
//-----------------------------inline_string_indexOfI-----------------------
bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) {
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
return false;
@ -1234,6 +1246,7 @@ bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) {
if (!Matcher::match_rule_supported(Op_StrIndexOf)) {
return false;
}
assert(callee()->signature()->size() == 5, "String.indexOf() has 5 arguments");
Node* src = argument(0); // byte[]
Node* src_count = argument(1); // char count
@ -1259,8 +1272,21 @@ bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) {
RegionNode* region = new RegionNode(5);
Node* phi = new PhiNode(region, TypeInt::INT);
Node* result = nullptr;
Node* result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count, region, phi, ae);
bool call_opt_stub = (StubRoutines::_string_indexof_array[ae] != nullptr);
if (call_opt_stub) {
assert(arrayOopDesc::base_offset_in_bytes(T_BYTE) >= 16, "Needed for indexOf");
Node* call = make_runtime_call(RC_LEAF, OptoRuntime::string_IndexOf_Type(),
StubRoutines::_string_indexof_array[ae],
"stringIndexOf", TypePtr::BOTTOM, src_start,
src_count, tgt_start, tgt_count);
result = _gvn.transform(new ProjNode(call, TypeFunc::Parms));
} else {
result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count,
region, phi, ae);
}
if (result != nullptr) {
// The result is index relative to from_index if substring was found, -1 otherwise.
// Generate code which will fold into cmove.

View File

@ -1357,6 +1357,27 @@ const TypeFunc* OptoRuntime::base64_encodeBlock_Type() {
const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields);
return TypeFunc::make(domain, range);
}
// String IndexOf function
const TypeFunc* OptoRuntime::string_IndexOf_Type() {
int argcnt = 4;
const Type** fields = TypeTuple::fields(argcnt);
int argp = TypeFunc::Parms;
fields[argp++] = TypePtr::NOTNULL; // haystack array
fields[argp++] = TypeInt::INT; // haystack length
fields[argp++] = TypePtr::NOTNULL; // needle array
fields[argp++] = TypeInt::INT; // needle length
assert(argp == TypeFunc::Parms + argcnt, "correct decoding");
const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields);
// result type needed
fields = TypeTuple::fields(1);
fields[TypeFunc::Parms + 0] = TypeInt::INT; // Index of needle in haystack
const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields);
return TypeFunc::make(domain, range);
}
// Base64 decode function
const TypeFunc* OptoRuntime::base64_decodeBlock_Type() {
int argcnt = 7;

View File

@ -297,6 +297,7 @@ private:
static const TypeFunc* chacha20Block_Type();
static const TypeFunc* base64_encodeBlock_Type();
static const TypeFunc* base64_decodeBlock_Type();
static const TypeFunc* string_IndexOf_Type();
static const TypeFunc* poly1305_processBlocks_Type();
static const TypeFunc* intpoly_montgomeryMult_P256_Type();
static const TypeFunc* intpoly_assign_Type();

View File

@ -149,6 +149,8 @@ address StubRoutines::_sha3_implCompressMB = nullptr;
address StubRoutines::_updateBytesCRC32 = nullptr;
address StubRoutines::_crc_table_adr = nullptr;
address StubRoutines::_string_indexof_array[4] = { nullptr };
address StubRoutines::_crc32c_table_addr = nullptr;
address StubRoutines::_updateBytesCRC32C = nullptr;
address StubRoutines::_updateBytesAdler32 = nullptr;

View File

@ -232,6 +232,8 @@ class StubRoutines: AllStatic {
static address _updateBytesCRC32;
static address _crc_table_adr;
static address _string_indexof_array[4];
static address _crc32c_table_addr;
static address _updateBytesCRC32C;
static address _updateBytesAdler32;

View File

@ -11,7 +11,7 @@
#
# A test flagged with cgroups uses cgroups.
#
# Notes on "client" keywords : headful sound printer multimon
# Notes on "client" keywords : headful sound printer multimon
# ===========================================================
#
# These keywords are there to help with test selection so that
@ -31,7 +31,7 @@
# Tests may not fail if there is none, instead just silently return.
# But they also may legitimately throw an Exception depending on the test.
# Also printer tests are not necessarily headful, but some are, and some are automated.
#
#
# "sound". Similarly, not all sound tests require audio devices, but many do.
# A test flagged with key "sound" needs audio devices on the system.
# Also they are not necessarily "headful", since they don't require a display etc.
@ -99,6 +99,7 @@ requires.properties= \
vm.jvmci \
vm.jvmci.enabled \
vm.jvmti \
vm.cpu.features \
docker.support \
release.implementor \
jdk.containerized \

View File

@ -0,0 +1,258 @@
/*
* Copyright (c) 2024, Intel Corporation. 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.
*/
/*
* @test
* @bug 8320448
* @summary test String indexOf() intrinsic
* @run driver IndexOf
*/
/*
* @test
* @bug 8320448
* @summary test String indexOf() intrinsic
* @requires vm.cpu.features ~= ".*avx2.*"
* @requires vm.compiler2.enabled
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xcomp -XX:-TieredCompilation -XX:UseAVX=2 -XX:+UnlockDiagnosticVMOptions -XX:+EnableX86ECoreOpts IndexOf
*/
public class IndexOf {
final int scope = 32*2+16+8;
final char a, aa, b, c, d;
enum Encoding {LL, UU, UL; }
final Encoding ae;
int failures;
IndexOf(Encoding _ae) {
failures = 0;
ae = _ae;
switch (ae) {
case LL:
a = 'a';
aa = a;
b = 'b';
c = 'c';
d = 'd';
break;
case UU:
a = '\u0061';
aa = a;
b = '\u0062';
c = '\u1063';
d = '\u0064';
break;
default: //case UL:
a = 'a';
aa = '\u1061';
b = 'b';
c = 'c';
d = 'd';
break;
}
}
// needle =~ /ab*d/
// badNeedle =~ /ab*db*d/
interface Append {void append(int pos, char cc);}
String newNeedle(int size, int badPosition) {
if (size<2) {throw new RuntimeException("Fix testcase "+size);}
StringBuilder needle = new StringBuilder(size);
Append n = (int pos, char cc) -> {
if (pos == badPosition)
needle.append(c);
else
needle.append(cc);
};
n.append(0, a);
for (int i=1; i<size-1; i++) {
n.append(i, b);
}
n.append(size-1, d);
return needle.toString();
}
// haystack =~ /a*{needle}d*/
String newHaystack(int size, String needle, int nPosition) {
if (nPosition+needle.length()>size) {throw new RuntimeException("Fix testcase "+nPosition+" "+needle.length()+" "+size);}
StringBuilder haystack = new StringBuilder(size);
int i = 0;
for (; i<nPosition; i++) {
haystack.append(aa);
}
haystack.append(needle);
i += needle.length();
for (; i<size; i++) {
haystack.append(d);
}
return haystack.toString();
}
// haystack =~ /a*{needle}+b*/
String newHaystackRepeat(int size, String needle, int nPosition) {
if (nPosition+needle.length()>size) {throw new RuntimeException("Fix testcase "+nPosition+" "+needle.length()+" "+size);}
StringBuilder haystack = new StringBuilder(size);
int i = 0;
for (; i<nPosition; i++) {
haystack.append(aa);
}
for (; i< nPosition+needle.length(); i += needle.length()) {
haystack.append(needle);
}
for (; i<size; i++) {
haystack.append(d);
}
return haystack.toString();
}
public static void main(String[] args) {
int failures = 0;
for (Encoding ae : Encoding.values()) {
failures += (new IndexOf(ae))
.test0()
.test1()
.test2()
.test3()
.test4()
.failures;
}
if (failures != 0) {
throw new RuntimeException("IndexOf test failed.");
}
}
// Need to disable checks in String.java if intrinsic is to be tested
IndexOf test0() { // Test 'trivial cases'
// if (0==needle_len) return haystack_off;
if (3 != "Hello".indexOf("", 3)) {
System.out.println("FAILED: if (0==needle_len) return haystack_off");
failures++;
}
//if (0==haystack_len) return -1;
if (-1 != "".indexOf("Hello", 3)) {
System.out.println("FAILED: if (0==haystack_len) return -1");
failures++;
}
//if (needle_len>haystack_len) return -1;
if (-1 != "Hello".indexOf("HelloWorld", 3)) {
System.out.println("FAILED: if (needle_len>haystack_len) return -1");
failures++;
}
return this;
}
IndexOf test1() { // Test expected to find one needle
for (int nSize = 2; nSize<scope; nSize++) {
String needle = newNeedle(nSize, -1);
for (int hSize = nSize; hSize<scope; hSize++) {
for (int i = 0; i<hSize-nSize; i++) {
String haystack = newHaystack(hSize, needle, i);
for (int j = 0; j<=i; j++) {
int found = haystack.indexOf(needle, j);
if (i != found) {
System.out.println("("+ae.name()+")(T1) Trying needle["+nSize+"] in haystack["+hSize+"] at offset["+i+"]");
System.out.println(" FAILED: Found " + needle + "@" + found + " in " + haystack + " from ["+j+"]");
failures++;
}
}
}
}
}
return this;
}
IndexOf test2() { // Test needle with one mismatched character
for (int nSize = 2; nSize<scope; nSize++) {
for (int hSize = nSize; hSize<scope; hSize++) {
String needle = newNeedle(nSize, -1);
for (int badPosition = 0; badPosition < nSize; badPosition+=1) {
String badNeedle = newNeedle(nSize, badPosition);
for (int i = 0; i<hSize-nSize; i++) {
String haystack = newHaystack(hSize, needle, i);
int found = haystack.indexOf(badNeedle, 1);
if (-1 != found) {
System.out.println("("+ae.name()+")(T2) Trying bad needle["+nSize+"]["+badPosition+"] in haystack["+hSize+"] at offset["+i+"]");
System.out.println(" FAILED: False " + found + " " + haystack + "["+needle+"]["+badNeedle+"]");
failures++;
}
}
}
}
}
return this;
}
IndexOf test3() { // Test expected to find first of the repeated needles
for (int nSize = 2; nSize<scope; nSize++) {
String needle = newNeedle(nSize, -1);
for (int hSize = nSize; hSize<scope; hSize++) {
for (int i = 0; i<hSize-nSize; i++) {
String haystack = newHaystackRepeat(hSize, needle, i);
for (int j = 0; j<=i; j++) {
int found = haystack.indexOf(needle, j);
if (i != found) {
System.out.println("("+ae.name()+")(T3) Trying repeaded needle["+nSize+"] in haystack["+hSize+"] at offset["+i+"]");
System.out.println(" FAILED: " + found + " " + haystack + "["+needle+"]");
failures++;
}
}
}
}
}
return this;
}
IndexOf test4() { // Test needle at unreachable offset
for (int nSize = 2; nSize<scope; nSize++) {
String needle = newNeedle(nSize, -1);
for (int hSize = nSize; hSize<scope; hSize++) {
for (int i = 0; i<hSize-nSize; i++) {
String haystack = newHaystack(hSize, needle, i);
// prefix lookup
for (int j = nSize-1; j<i+nSize; j++) {
int found = haystack.indexOf(needle, 0, j);
if (-1 != found) {
System.out.println("("+ae.name()+")(T4) Trying needle["+nSize+"] at offset ["+i+"] in haystack["+hSize+"] upto ["+j+"]");
System.out.println(" FAILED: False " + found + " " + haystack + "["+needle+"]");
failures++;
}
}
// sufix lookup
for (int j = i+1; j<hSize; j++) {
int found = haystack.indexOf(needle, j);
if (-1 != found) {
System.out.println("("+ae.name()+")(T4) Trying needle["+nSize+"] at offset ["+i+"] in haystack["+hSize+"] from ["+j+"]");
System.out.println(" FAILED: False " + found + " " + haystack + "["+needle+"]");
failures++;
}
}
}
}
}
return this;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,273 @@
/*
* Copyright (c) 2024, 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.
*/
package org.openjdk.bench.java.lang;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(value = 3)
public class StringIndexOfHuge {
private String dataString;
private String dataString16;
private String dataStringHuge;
private String dataStringHuge16;
private String earlyMatchString;
private String earlyMatchString16;
private String midMatchString;
private String midMatchString16;
private String lateMatchString;
private String lateMatchString16;
private String searchString;
private String searchString16;
private String searchStringSmall;
private String searchStringSmall16;
private String searchStringHuge;
private String searchStringHuge16;
private String searchNoMatch;
private String searchNoMatch16;
private String Amdahl_1;
private String Amdahl_2;
private String Amdahl_3;
private String Amdahl_4;
private String Amdahl_5;
private String Amdahl_6;
@Setup
public void setup() {
dataString = "ngdflsoscargfdgf";
dataString16 = "ngdfilso\u01facargfd\u01eef";
dataStringHuge = (("A".repeat(32) + "B".repeat(32)).repeat(16) + "X").repeat(2) + "bB";
dataStringHuge16 = "\u01de" + (("A".repeat(32) + "B".repeat(32)).repeat(16) + "\u01fe").repeat(2) + "\u01eeB";
earlyMatchString = dataStringHuge.substring(0, 34);
earlyMatchString16 = dataStringHuge16.substring(0, 34);
midMatchString = dataStringHuge.substring(dataStringHuge.length() / 2 - 16, dataStringHuge.length() / 2 + 17);
midMatchString16 = dataStringHuge16.substring(dataStringHuge16.length() / 2 - 16, dataStringHuge16.length() / 2 + 17);
lateMatchString = dataStringHuge.substring(dataStringHuge.length() - 31);
lateMatchString16 = dataStringHuge16.substring(dataStringHuge16.length() - 31);
searchString = "oscar";
searchString16 = "o\u01facar";
searchStringSmall = "dgf";
searchStringSmall16 = "d\u01eef";
searchStringHuge = "capaapapapasdkajdlkajskldjaslkajdlkajskldjaslkjdlkasjdsalk";
searchStringHuge16 = "capaapapapasdkajdlka\u01feskldjaslkajdlkajskldjaslkjdlkasjdsalk";
searchNoMatch = "XYXyxYxy".repeat(22);
searchNoMatch16 = "\u01ab\u01ba\u01cb\u01bc\u01de\u01ed\u01fa\u01af".repeat(22);
Amdahl_1 = "B".repeat(30) + "X" + "A".repeat(30);
Amdahl_2 = "A".repeat(32) + "F" + "B".repeat(32);
Amdahl_3 = "A".repeat(32) + "B".repeat(32) + "XbB";
Amdahl_4 = "B".repeat(30) + "\u01ef" + "A".repeat(30);
Amdahl_5 = "A".repeat(32) + "\u01ef" + "B".repeat(32);
Amdahl_6 = "A".repeat(32) + "B".repeat(32) + "\u01fe\u01eeB";
}
/** IndexOf Micros */
@Benchmark
public int searchHugeEarlyMatch() {
return dataStringHuge.indexOf(earlyMatchString);
}
@Benchmark
public int searchHugeMiddleMatch() {
return dataStringHuge.indexOf(midMatchString);
}
@Benchmark
public int searchHugeLateMatch() {
return dataStringHuge.indexOf(lateMatchString);
}
@Benchmark
public int searchHugeNoMatch() {
return dataStringHuge.indexOf(searchNoMatch);
}
@Benchmark
public int searchSmallEarlyMatch() {
return searchString.indexOf(searchString);
}
@Benchmark
public int searchSmallMidMatch() {
return dataString.indexOf(searchString);
}
@Benchmark
public int searchSmallLateMatch() {
return dataString.indexOf(searchStringSmall);
}
@Benchmark
public int searchHugeLargeSubstring() {
return dataStringHuge.indexOf(Amdahl_1, 74);
}
@Benchmark
public int searchHugeLargeSubstringNoMatch() {
return dataStringHuge.indexOf(Amdahl_2, 64);
}
@Benchmark
public int searchSubstringLongerThanString() {
return midMatchString.indexOf(dataStringHuge, 3);
}
@Benchmark
public int searchHugeWorstCase() {
return dataStringHuge.indexOf(Amdahl_3);
}
@Benchmark
public int search16HugeEarlyMatch() {
return dataStringHuge16.indexOf(earlyMatchString);
}
@Benchmark
public int search16HugeMiddleMatch() {
return dataStringHuge16.indexOf(midMatchString);
}
@Benchmark
public int search16HugeLateMatch() {
return dataStringHuge16.indexOf(lateMatchString);
}
@Benchmark
public int search16HugeNoMatch() {
return dataStringHuge16.indexOf(searchNoMatch);
}
@Benchmark
public int search16SmallEarlyMatch() {
return searchString16.indexOf(searchString);
}
@Benchmark
public int search16SmallMidMatch() {
return dataString16.indexOf(searchString);
}
@Benchmark
public int search16SmallLateMatch() {
return dataString16.indexOf(searchStringSmall);
}
@Benchmark
public int search16HugeLargeSubstring() {
return dataStringHuge16.indexOf(Amdahl_1, 74);
}
@Benchmark
public int search16HugeLargeSubstringNoMatch() {
return dataStringHuge16.indexOf(Amdahl_2, 64);
}
@Benchmark
public int search16SubstringLongerThanString() {
return midMatchString16.indexOf(dataStringHuge, 3);
}
@Benchmark
public int search16HugeWorstCase() {
return dataStringHuge16.indexOf(Amdahl_3);
}
@Benchmark
public int search16HugeEarlyMatch16() {
return dataStringHuge16.indexOf(earlyMatchString16);
}
@Benchmark
public int search16HugeMiddleMatch16() {
return dataStringHuge16.indexOf(midMatchString16);
}
@Benchmark
public int search16HugeLateMatch16() {
return dataStringHuge16.indexOf(lateMatchString16);
}
@Benchmark
public int search16HugeNoMatch16() {
return dataStringHuge16.indexOf(searchNoMatch16);
}
@Benchmark
public int search16SmallEarlyMatch16() {
return searchString16.indexOf(searchString16);
}
@Benchmark
public int search16SmallMidMatch16() {
return dataString16.indexOf(searchString16);
}
@Benchmark
public int search16SmallLateMatch16() {
return dataString16.indexOf(searchStringSmall16);
}
@Benchmark
public int search16HugeLargeSubstring16() {
return dataStringHuge16.indexOf(Amdahl_4, 74);
}
@Benchmark
public int search16HugeLargeSubstringNoMatch16() {
return dataStringHuge16.indexOf(Amdahl_5, 64);
}
@Benchmark
public int search16SubstringLongerThanString16() {
return midMatchString16.indexOf(dataStringHuge16, 3);
}
@Benchmark
public int search16HugeWorstCase16() {
return dataStringHuge16.indexOf(Amdahl_6);
}
}