Use vectorized index_* procs in core

This commit is contained in:
Feoramund
2024-08-06 15:19:05 -04:00
parent 8deeb40e5d
commit f66fcd9acb
2 changed files with 78 additions and 16 deletions
+31
View File
@@ -1,6 +1,8 @@
package bytes package bytes
import "base:intrinsics"
import "core:mem" import "core:mem"
@require import simd_util "core:simd/util"
import "core:unicode" import "core:unicode"
import "core:unicode/utf8" import "core:unicode/utf8"
@@ -295,6 +297,7 @@ split_after_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
index_byte :: proc(s: []byte, c: byte) -> int { index_byte :: proc(s: []byte, c: byte) -> int {
_index_byte :: #force_inline proc(s: []byte, c: byte) -> int {
for i := 0; i < len(s); i += 1 { for i := 0; i < len(s); i += 1 {
if s[i] == c { if s[i] == c {
return i return i
@@ -303,8 +306,25 @@ index_byte :: proc(s: []byte, c: byte) -> int {
return -1 return -1
} }
// NOTE(Feoramund): On my Alder Lake CPU, I have only witnessed a
// significant speedup when compiling in either Size or Speed mode.
// The SIMD version is usually 2-3x slower without optimizations on.
when ODIN_OPTIMIZATION_MODE > .Minimal && intrinsics.has_target_feature("sse2") {
// SIMD's benefits are noticeable only past a certain threshold of data.
// For small data, use the plain old algorithm.
if len(s) >= simd_util.RECOMMENDED_SCAN_SIZE {
return simd_util.index_byte(s, c)
} else {
return _index_byte(s, c)
}
} else {
return _index_byte(s, c)
}
}
// Returns -1 if c is not present // Returns -1 if c is not present
last_index_byte :: proc(s: []byte, c: byte) -> int { last_index_byte :: proc(s: []byte, c: byte) -> int {
_last_index_byte :: #force_inline proc(s: []byte, c: byte) -> int {
for i := len(s)-1; i >= 0; i -= 1 { for i := len(s)-1; i >= 0; i -= 1 {
if s[i] == c { if s[i] == c {
return i return i
@@ -313,6 +333,17 @@ last_index_byte :: proc(s: []byte, c: byte) -> int {
return -1 return -1
} }
when ODIN_OPTIMIZATION_MODE > .Minimal && intrinsics.has_target_feature("sse2") {
if len(s) >= simd_util.RECOMMENDED_SCAN_SIZE {
return simd_util.last_index_byte(s, c)
} else {
return _last_index_byte(s, c)
}
} else {
return _last_index_byte(s, c)
}
}
@private PRIME_RABIN_KARP :: 16777619 @private PRIME_RABIN_KARP :: 16777619
+31
View File
@@ -1,7 +1,9 @@
// Procedures to manipulate UTF-8 encoded strings // Procedures to manipulate UTF-8 encoded strings
package strings package strings
import "base:intrinsics"
import "core:io" import "core:io"
@require import simd_util "core:simd/util"
import "core:mem" import "core:mem"
import "core:unicode" import "core:unicode"
import "core:unicode/utf8" import "core:unicode/utf8"
@@ -1424,6 +1426,7 @@ Output:
*/ */
index_byte :: proc(s: string, c: byte) -> (res: int) { index_byte :: proc(s: string, c: byte) -> (res: int) {
_index_byte :: #force_inline proc(s: string, c: byte) -> int {
for i := 0; i < len(s); i += 1 { for i := 0; i < len(s); i += 1 {
if s[i] == c { if s[i] == c {
return i return i
@@ -1431,6 +1434,22 @@ index_byte :: proc(s: string, c: byte) -> (res: int) {
} }
return -1 return -1
} }
// NOTE(Feoramund): On my Alder Lake CPU, I have only witnessed a
// significant speedup when compiling in either Size or Speed mode.
// The SIMD version is usually 2-3x slower without optimizations on.
when ODIN_OPTIMIZATION_MODE > .Minimal && intrinsics.has_target_feature("sse2") {
// SIMD's benefits are noticeable only past a certain threshold of data.
// For small data, use the plain old algorithm.
if len(s) >= simd_util.RECOMMENDED_SCAN_SIZE {
return simd_util.index_byte(transmute([]u8)s, c)
} else {
return _index_byte(s, c)
}
} else {
return _index_byte(s, c)
}
}
/* /*
Returns the byte offset of the last byte `c` in the string `s`, -1 when not found. Returns the byte offset of the last byte `c` in the string `s`, -1 when not found.
@@ -1464,6 +1483,7 @@ Output:
*/ */
last_index_byte :: proc(s: string, c: byte) -> (res: int) { last_index_byte :: proc(s: string, c: byte) -> (res: int) {
_last_index_byte :: #force_inline proc(s: string, c: byte) -> int {
for i := len(s)-1; i >= 0; i -= 1 { for i := len(s)-1; i >= 0; i -= 1 {
if s[i] == c { if s[i] == c {
return i return i
@@ -1471,6 +1491,17 @@ last_index_byte :: proc(s: string, c: byte) -> (res: int) {
} }
return -1 return -1
} }
when ODIN_OPTIMIZATION_MODE > .Minimal && intrinsics.has_target_feature("sse2") {
if len(s) >= simd_util.RECOMMENDED_SCAN_SIZE {
return simd_util.last_index_byte(transmute([]u8)s, c)
} else {
return _last_index_byte(s, c)
}
} else {
return _last_index_byte(s, c)
}
}
/* /*
Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found. Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found.
Invalid runes return -1 Invalid runes return -1