From e7899f0bc28778a515c4a9e383a3e74a72851e4b Mon Sep 17 00:00:00 2001 From: araq Date: Sat, 7 Mar 2026 22:32:57 +0100 Subject: [PATCH] faster code --- lib/system/strmantle.nim | 69 ++++++++++++++++++++-------------------- lib/system/strs_v3.nim | 33 ++++++++++++++++--- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 92a8653f2a..8f837f5f92 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -10,45 +10,46 @@ # Compilerprocs for strings that do not depend on the string implementation. import std/private/digitsutils as digitsutils2 -proc cmpStrings(a, b: string): int {.inline, compilerproc.} = - let alen = a.len - let blen = b.len - let minlen = min(alen, blen) - if minlen > 0: - result = c_memcmp(unsafeAddr a[0], unsafeAddr b[0], cast[csize_t](minlen)).int - if result == 0: +when not defined(nimsso): + proc cmpStrings(a, b: string): int {.inline, compilerproc.} = + let alen = a.len + let blen = b.len + let minlen = min(alen, blen) + if minlen > 0: + result = c_memcmp(unsafeAddr a[0], unsafeAddr b[0], cast[csize_t](minlen)).int + if result == 0: + result = alen - blen + else: result = alen - blen - else: - result = alen - blen -proc leStrings(a, b: string): bool {.inline, compilerproc.} = - # required by upcoming backends (NIR). - cmpStrings(a, b) <= 0 + proc leStrings(a, b: string): bool {.inline, compilerproc.} = + # required by upcoming backends (NIR). + cmpStrings(a, b) <= 0 -proc ltStrings(a, b: string): bool {.inline, compilerproc.} = - # required by upcoming backends (NIR). - cmpStrings(a, b) < 0 + proc ltStrings(a, b: string): bool {.inline, compilerproc.} = + # required by upcoming backends (NIR). + cmpStrings(a, b) < 0 -proc eqStrings(a, b: string): bool {.inline, compilerproc.} = - result = false - let alen = a.len - let blen = b.len - if alen == blen: - if alen == 0: return true - return equalMem(unsafeAddr(a[0]), unsafeAddr(b[0]), alen) + proc eqStrings(a, b: string): bool {.inline, compilerproc.} = + result = false + let alen = a.len + let blen = b.len + if alen == blen: + if alen == 0: return true + return equalMem(unsafeAddr(a[0]), unsafeAddr(b[0]), alen) -proc hashString(s: string): int {.compilerproc.} = - # the compiler needs exactly the same hash function! - # this used to be used for efficient generation of string case statements - var h = 0'u - for i in 0..len(s)-1: - h = h + uint(s[i]) - h = h + h shl 10 - h = h xor (h shr 6) - h = h + h shl 3 - h = h xor (h shr 11) - h = h + h shl 15 - result = cast[int](h) + proc hashString(s: string): int {.compilerproc.} = + # the compiler needs exactly the same hash function! + # this used to be used for efficient generation of string case statements + var h = 0'u + for i in 0..len(s)-1: + h = h + uint(s[i]) + h = h + h shl 10 + h = h xor (h shr 6) + h = h + h shl 3 + h = h xor (h shr 11) + h = h + h shl 15 + result = cast[int](h) proc eqCstrings(a, b: cstring): bool {.inline, compilerproc.} = if pointer(a) == pointer(b): result = true diff --git a/lib/system/strs_v3.nim b/lib/system/strs_v3.nim index 72d5a9dc11..6ea1bf82f6 100644 --- a/lib/system/strs_v3.nim +++ b/lib/system/strs_v3.nim @@ -96,7 +96,7 @@ proc nimStrPutV3*(s: var SmallString; i: int; c: char) {.compilerproc, inline.} if i < AlwaysAvail: s.payload[i] = c -proc cmp(a, b: SmallString): int = +proc cmp(a, b: SmallString): int {.inline.} = # Use slen directly for prefix length: for short/medium it is the real length, # for long it is the sentinel (> AlwaysAvail), so min(..., AlwaysAvail) still gives 7. # This avoids dereferencing `more` before the prefix comparison. @@ -116,7 +116,7 @@ proc cmp(a, b: SmallString): int = if result == 0: result = la - lb -proc `==`(a, b: SmallString): bool = +proc `==`(a, b: SmallString): bool {.inline.} = if a.slen != b.slen: return false # slen equal: for short/medium this means equal lengths; for long (both sentinel) we still need fullLen. let slen = int(a.slen) @@ -392,7 +392,7 @@ proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} = s.more.data[newLen] = '\0' s.more.fullLen = newLen -proc nimAsgnStrV2(a: var SmallString; b: SmallString) {.compilerRtl.} = +proc nimAsgnStrV2(a: var SmallString; b: SmallString) {.compilerRtl, inline.} = if int(b.slen) <= PayloadSize: nimDestroyStrV1(a) copyMem(addr a, unsafeAddr b, sizeof(SmallString)) @@ -453,8 +453,31 @@ proc nimStrData(s: var SmallString): ptr UncheckedArray[char] {.compilerproc, in if slen > PayloadSize: cast[ptr UncheckedArray[char]](addr s.more.data[0]) else: cast[ptr UncheckedArray[char]](addr s.payload[0]) -proc eqStrings(a, b: SmallString): bool {.compilerproc, inline.} = a == b +# These take `string` (tyString) so the codegen uses them directly, bypassing +# strmantle.nim's versions which go through nimStrLen/nimStrAtMutV3 compilerproc calls. +proc cmpStrings(a, b: string): int {.compilerproc, inline.} = + cmp(cast[ptr SmallString](unsafeAddr a)[], cast[ptr SmallString](unsafeAddr b)[]) -proc cmpStrings(a, b: SmallString): int {.compilerproc, inline.} = cmp(a, b) +proc eqStrings(a, b: string): bool {.compilerproc, inline.} = + cast[ptr SmallString](unsafeAddr a)[] == cast[ptr SmallString](unsafeAddr b)[] + +proc leStrings(a, b: string): bool {.compilerproc, inline.} = + cmpStrings(a, b) <= 0 + +proc ltStrings(a, b: string): bool {.compilerproc, inline.} = + cmpStrings(a, b) < 0 + +proc hashString(s: string): int {.compilerproc.} = + let ss = cast[ptr SmallString](unsafeAddr s)[] + let (L, data) = ss.guts + var h = 0'u + for i in 0..