Skip to content

Commit

Permalink
Performance tweak and updated benchmarks (#11)
Browse files Browse the repository at this point in the history
* Small performance tweaks
* Updated benchmarks
* doc updates
  • Loading branch information
liyanchang authored Jan 14, 2023
1 parent c8ac0fd commit 879920e
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 44 deletions.
39 changes: 21 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Example usage

String strcase is pretty straight forward and there are a number of methods to
do it. This package is fully featured, more customizable, better tested, and
faster* than other packages and what you would probably whip up yourself.
faster than other packages and what you would probably whip up yourself.

### Unicode support

Expand Down Expand Up @@ -103,42 +103,45 @@ or reword my commentary based on suggestions or updates.


// This package - faster then almost all libraries
// Initialisms are more complicated and slightly slower, but still faster then other libraries that do less
BenchmarkToTitle-4 7821166 221 ns/op 32 B/op 1 allocs/op
BenchmarkToSnake-4 9378589 202 ns/op 32 B/op 1 allocs/op
BenchmarkToSNAKE-4 6174453 223 ns/op 32 B/op 1 allocs/op
BenchmarkToGoSnake-4 3114266 434 ns/op 44 B/op 4 allocs/op
BenchmarkToCustomCaser-4 2973855 448 ns/op 56 B/op 4 allocs/op
// Initialisms are more complicated and slightly slower, but still fast
BenchmarkToTitle-96 9617142 125.7 ns/op 16 B/op 1 allocs/op
BenchmarkToSnake-96 10659919 120.7 ns/op 16 B/op 1 allocs/op
BenchmarkToSNAKE-96 9018282 126.4 ns/op 16 B/op 1 allocs/op
BenchmarkToGoSnake-96 4903687 254.5 ns/op 26 B/op 4 allocs/op
BenchmarkToCustomCaser-96 4434489 265.0 ns/op 28 B/op 4 allocs/op

// Segment has very fast snake case and camel case libraries
// No features or customization, but very very fast
BenchmarkSegment-4 24003495 64.9 ns/op 16 B/op 1 allocs/op
BenchmarkSegment-96 33625734 35.54 ns/op 16 B/op 1 allocs/op

// Stdlib strings.Title for comparison, even though it only splits on spaces
BenchmarkToTitleStrings-4 11259376 161 ns/op 16 B/op 1 allocs/op
// Iancoleman has gotten some performance improvements, but remains
// without unicode support and lacks fine-grained customization
BenchmarkToSnakeIan-96 13141522 92.99 ns/op 16 B/op 1 allocs/op

// Stdlib strings.Title is deprecated; using golang.org/x.text
BenchmarkGolangOrgXTextCases-96 4665676 262.5 ns/op 272 B/op 2 allocs/op

// Other libraries or code snippets
// - Most are slower, by up to an order of magnitude
// - None support initialisms or customization
// - No support for initialisms or customization
// - Some generate only camelCase or snake_case
// - Many lack unicode support
BenchmarkToSnakeStoewer-4 7103268 297 ns/op 64 B/op 2 allocs/op
BenchmarkToSnakeStoewer-96 8095468 148.9 ns/op 64 B/op 2 allocs/op
// Copying small rune arrays is slow
BenchmarkToSnakeSiongui-4 3710768 413 ns/op 48 B/op 10 allocs/op
BenchmarkGoValidator-4 2416479 1049 ns/op 184 B/op 9 allocs/op
BenchmarkToSnakeSiongui-96 2912593 401.7 ns/op 112 B/op 19 allocs/op
BenchmarkGoValidator-96 3493800 342.6 ns/op 184 B/op 9 allocs/op
// String alloction is slow
BenchmarkToSnakeFatih-4 1000000 2407 ns/op 624 B/op 26 allocs/op
BenchmarkToSnakeIanColeman-4 1005766 1426 ns/op 160 B/op 13 allocs/op
BenchmarkToSnakeFatih-96 1282648 945.1 ns/op 616 B/op 26 allocs/op
// Regexp is slow
BenchmarkToSnakeGolangPrograms-4 614689 2237 ns/op 225 B/op 11 allocs/op
BenchmarkToSnakeGolangPrograms-96 778674 1495 ns/op 227 B/op 11 allocs/op

// These results aren't a surprise - my initial version of this library was
// painfully slow. I think most of us, without spending some time with
// profilers and benchmarks, would write also something on the slower side.

### Zero dependencies

That's right - zero. We only import Go standard library. No hassles with
That's right - zero. We only import the Go standard library. No hassles with
dependencies, licensing, security alerts.

## Why not this package
Expand Down
19 changes: 12 additions & 7 deletions convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func convertWithoutInitialisms(input string, delimiter rune, wordCase WordCase)
}

var b strings.Builder
b.Grow(len(input) * 2) // In case we need to write delimiters where they weren't before
b.Grow(len(input) + 4) // In case we need to write delimiters where they weren't before

var prev, curr rune
next := runes[0] // 0 length will have already returned so safe to index
Expand Down Expand Up @@ -91,7 +91,7 @@ func convertWithoutInitialisms(input string, delimiter rune, wordCase WordCase)
// Must be original case
b.WriteRune(curr)
}
inWord = inWord || true
inWord = true
}
return b.String()
}
Expand All @@ -108,7 +108,7 @@ func convertWithGoInitialisms(input string, delimiter rune, wordCase WordCase) s
}

var b strings.Builder
b.Grow(len(input) * 2) // In case we need to write delimiters where they weren't before
b.Grow(len(input) + 4) // In case we need to write delimiters where they weren't before

firstWord := true

Expand All @@ -124,10 +124,14 @@ func convertWithGoInitialisms(input string, delimiter rune, wordCase WordCase) s
// Don't bother with initialisms if the word is longer than 5
// A quick proxy to avoid the extra memory allocations
if end-start <= 5 {
key := strings.ToUpper(string(runes[start:end]))
if golintInitialisms[key] {
var word strings.Builder
word.Grow(end - start)
for i := start; i < end; i++ {
word.WriteRune(toUpper(runes[i]))
}
if golintInitialisms[word.String()] {
if !firstWord || wordCase != CamelCase {
b.WriteString(key)
b.WriteString(word.String())
firstWord = false
return
}
Expand Down Expand Up @@ -201,7 +205,7 @@ func convert(input string, fn SplitFn, delimiter rune, wordCase WordCase,
}

var b strings.Builder
b.Grow(len(input) * 2) // In case we need to write delimiters where they weren't before
b.Grow(len(input) + 4) // In case we need to write delimiters where they weren't before

firstWord := true
var skipIndexes []int
Expand All @@ -224,6 +228,7 @@ func convert(input string, fn SplitFn, delimiter rune, wordCase WordCase,
// I'm open to it if there is a use case
if initialisms != nil {
var word strings.Builder
word.Grow(end - start)
for i := start; i < end; i++ {
word.WriteRune(toUpper(runes[i]))
}
Expand Down
40 changes: 21 additions & 19 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Example usage
String strcase is pretty straight forward and there are a number of methods to
do it. This package is fully featured, more customizable, better tested, and
faster* than other packages and what you would probably whip up yourself.
faster than other packages and what you would probably whip up yourself.
### Unicode support
Expand Down Expand Up @@ -87,43 +87,45 @@ Hopefully I was fair to each library and happy to rerun benchmarks differently
or reword my commentary based on suggestions or updates.
// This package - faster then almost all libraries
// Initialisms are more complicated and slightly slower, but still faster then other libraries that do less
BenchmarkToTitle-4 7821166 221 ns/op 32 B/op 1 allocs/op
BenchmarkToSnake-4 9378589 202 ns/op 32 B/op 1 allocs/op
BenchmarkToSNAKE-4 6174453 223 ns/op 32 B/op 1 allocs/op
BenchmarkToGoSnake-4 3114266 434 ns/op 44 B/op 4 allocs/op
BenchmarkToCustomCaser-4 2973855 448 ns/op 56 B/op 4 allocs/op
// Initialisms are more complicated and slightly slower, but still fast
BenchmarkToTitle-96 9617142 125.7 ns/op 16 B/op 1 allocs/op
BenchmarkToSnake-96 10659919 120.7 ns/op 16 B/op 1 allocs/op
BenchmarkToSNAKE-96 9018282 126.4 ns/op 16 B/op 1 allocs/op
BenchmarkToGoSnake-96 4903687 254.5 ns/op 26 B/op 4 allocs/op
BenchmarkToCustomCaser-96 4434489 265.0 ns/op 28 B/op 4 allocs/op
// Segment has very fast snake case and camel case libraries
// No features or customization, but very very fast
BenchmarkSegment-4 24003495 64.9 ns/op 16 B/op 1 allocs/op
BenchmarkSegment-96 33625734 35.54 ns/op 16 B/op 1 allocs/op
// Stdlib strings.Title for comparison, even though it only splits on spaces
BenchmarkToTitleStrings-4 11259376 161 ns/op 16 B/op 1 allocs/op
// Iancoleman has gotten some performance improvements, but remains
// without unicode support and lacks fine-grained customization
BenchmarkToSnakeIan-96 13141522 92.99 ns/op 16 B/op 1 allocs/op
// Stdlib strings.Title is deprecated; using golang.org/x.text
BenchmarkGolangOrgXTextCases-96 4665676 262.5 ns/op 272 B/op 2 allocs/op
// Other libraries or code snippets
// - Most are slower, by up to an order of magnitude
// - None support initialisms or customization
// - No support for initialisms or customization
// - Some generate only camelCase or snake_case
// - Many lack unicode support
BenchmarkToSnakeStoewer-4 7103268 297 ns/op 64 B/op 2 allocs/op
BenchmarkToSnakeStoewer-96 8095468 148.9 ns/op 64 B/op 2 allocs/op
// Copying small rune arrays is slow
BenchmarkToSnakeSiongui-4 3710768 413 ns/op 48 B/op 10 allocs/op
BenchmarkGoValidator-4 2416479 1049 ns/op 184 B/op 9 allocs/op
BenchmarkToSnakeSiongui-96 2912593 401.7 ns/op 112 B/op 19 allocs/op
BenchmarkGoValidator-96 3493800 342.6 ns/op 184 B/op 9 allocs/op
// String alloction is slow
BenchmarkToSnakeFatih-4 1000000 2407 ns/op 624 B/op 26 allocs/op
BenchmarkToSnakeIanColeman-4 1005766 1426 ns/op 160 B/op 13 allocs/op
BenchmarkToSnakeFatih-96 1282648 945.1 ns/op 616 B/op 26 allocs/op
// Regexp is slow
BenchmarkToSnakeGolangPrograms-4 614689 2237 ns/op 225 B/op 11 allocs/op
BenchmarkToSnakeGolangPrograms-96 778674 1495 ns/op 227 B/op 11 allocs/op
// These results aren't a surprise - my initial version of this library was
// painfully slow. I think most of us, without spending some time with
// profilers and benchmarks, would write also something on the slower side.
### Zero dependencies
That's right - zero. We only import Go standard library. No hassles with
That's right - zero. We only import the Go standard library. No hassles with
dependencies, licensing, security alerts.
## Why not this package
Expand Down

0 comments on commit 879920e

Please sign in to comment.