8320448: Accelerate IndexOf using AVX2
Reviewed-by: epeter, kvn, sviswanathan
This commit is contained in:
parent
25ad86234a
commit
8e72d7cf8e
@ -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);
|
||||
|
@ -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,
|
||||
|
1837
src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp
Normal file
1837
src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
};
|
||||
|
||||
|
@ -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 ||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 \
|
||||
|
258
test/jdk/java/lang/String/IndexOf.java
Normal file
258
test/jdk/java/lang/String/IndexOf.java
Normal 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;
|
||||
}
|
||||
}
|
1381
test/jdk/java/lang/StringBuffer/ECoreIndexOf.java
Normal file
1381
test/jdk/java/lang/StringBuffer/ECoreIndexOf.java
Normal file
File diff suppressed because it is too large
Load Diff
273
test/micro/org/openjdk/bench/java/lang/StringIndexOfHuge.java
Normal file
273
test/micro/org/openjdk/bench/java/lang/StringIndexOfHuge.java
Normal 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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user