You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2026-06-03 18:35:08 +02:00
9587c57d48
This PR refactors the internal slice conversion helpers in
`attribute/internal` to use generics and adds explicit short-length fast
paths for slice-backed attribute values.
The main changes are:
- replace the old type-specific internal helpers with generic
`SliceValue[T]` and `AsSlice[T]`
- preserve the comparable-array storage model used by `Value` and
`KeyValue`
- add fixed-size fast paths for lengths `0..3`
- add a matching short-length fast path in `IntSliceValue` before
falling back to `[]int64` conversion
- keep reflection as the fallback for larger slice lengths
- keep the existing `Value.As*Slice()` methods in `attribute` as thin
wrappers over the generic internal helpers
- add type-mismatch coverage for `AsSlice[T]`
- expand the benchmark suite so it measures both:
- short slices that hit the new fixed-size path
- longer slices that still use the reflective fallback
## Rationale
The package still needs reflection for arbitrary runtime lengths because
slice values are stored as comparable arrays behind `any`, and Go cannot
construct a runtime-sized array type without `reflect.ArrayOf`.
The fast path is intentionally small. The cutoff of `0..3` is based on a
combination of:
- benchmark gains for short slices
- semantic convention examples where `1..3` values are common
- downstream source analysis, which found that most external call sites
pass variables rather than inline literals, so static source scanning
does not justify a larger cutoff
## External Usage Validation
Downstream scan:
- sampled repos cloned and scanned: `285`
- most external call sites pass dynamic slice variables, not inline
literals
- final literal counts recovered statically:
- `StringSlice`: total `131`, dynamic `121`, literal len `0`: `1`, len
`1`: `8`, len `2`: `1`
- `IntSlice`: total `5`, all dynamic
- `Int64Slice`: total `6`, all dynamic
- `BoolSlice`: total `3`, all dynamic
- `Float64Slice`: total `3`, all dynamic
Semantic conventions reviewed locally in `semantic-conventions` include
several slice-valued attributes where short lists are normal, for
example:
- `browser.brands`
- `gen_ai.request.stop_sequences`
- `gen_ai.request.encoding_formats`
- `gen_ai.response.finish_reasons`
- `user.roles`
- `file.attributes`
There are also clearly unbounded cases like headers, metadata, command
args, and some cloud/provider arrays. That combination supports a small
fast path, but not an assumption that all real-world slices are tiny.
## Benchmarks
Changes to the benchmarks:
- internal microbenchmarks now run both `Len2` and `Len8`
- public `attribute` benchmarks now run both `Len2` and `Len8`
- repeated benchmark runs were compared with `benchstat`
`Len2` exercises the new fixed-size path. `Len8` exercises the
reflective fallback path.
Headline result:
- short slices improve substantially
- large slices stay close to baseline because they still use reflection
- `IntSlice` now gets the same short-slice win as the other slice types,
but larger `[]int` values still pay the `[]int` to `[]int64` conversion
cost
### Internal Helpers
```text
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/attribute/internal
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
│ /tmp/bench-internal-base-v2.txt │ /tmp/bench-internal-current-v2.txt │
│ sec/op │ sec/op vs base │
BoolSliceValue/Len2-8 124.00n ± 4% 19.36n ± 12% -84.39% (p=0.000 n=12)
BoolSliceValue/Len8-8 129.9n ± 2% 136.8n ± 3% +5.31% (p=0.000 n=12)
Int64SliceValue/Len2-8 146.00n ± 26% 26.13n ± 3% -82.10% (p=0.000 n=12)
Int64SliceValue/Len8-8 172.1n ± 4% 174.9n ± 5% ~ (p=0.443 n=12)
Float64SliceValue/Len2-8 151.70n ± 2% 26.25n ± 2% -82.70% (p=0.000 n=12)
Float64SliceValue/Len8-8 173.6n ± 2% 169.7n ± 4% ~ (p=0.155 n=12)
StringSliceValue/Len2-8 177.15n ± 2% 42.03n ± 3% -76.27% (p=0.000 n=12)
StringSliceValue/Len8-8 217.2n ± 6% 219.1n ± 6% ~ (p=0.504 n=12)
AsFloat64Slice/Len2-8 96.77n ± 3% 63.05n ± 27% -34.84% (p=0.000 n=12)
AsFloat64Slice/Len8-8 123.8n ± 18% 117.1n ± 4% ~ (p=1.000 n=12)
geomean 147.6n 71.85n -51.33%
│ /tmp/bench-internal-base-v2.txt │ /tmp/bench-internal-current-v2.txt │
│ B/op │ B/op vs base │
BoolSliceValue/Len2-8 4.000 ± 0% 2.000 ± 0% -50.00% (p=0.000 n=12)
BoolSliceValue/Len8-8 16.00 ± 0% 16.00 ± 0% ~ (p=1.000 n=12) ¹
Int64SliceValue/Len2-8 32.00 ± 0% 16.00 ± 0% -50.00% (p=0.000 n=12)
Int64SliceValue/Len8-8 128.0 ± 0% 128.0 ± 0% ~ (p=1.000 n=12) ¹
Float64SliceValue/Len2-8 32.00 ± 0% 16.00 ± 0% -50.00% (p=0.000 n=12)
Float64SliceValue/Len8-8 128.0 ± 0% 128.0 ± 0% ~ (p=1.000 n=12) ¹
StringSliceValue/Len2-8 64.00 ± 0% 32.00 ± 0% -50.00% (p=0.000 n=12)
StringSliceValue/Len8-8 256.0 ± 0% 256.0 ± 0% ~ (p=1.000 n=12) ¹
AsFloat64Slice/Len2-8 40.00 ± 0% 40.00 ± 0% ~ (p=1.000 n=12) ¹
AsFloat64Slice/Len8-8 88.00 ± 0% 88.00 ± 0% ~ (p=1.000 n=12) ¹
geomean 47.77 36.21 -24.21%
¹ all samples are equal
│ /tmp/bench-internal-base-v2.txt │ /tmp/bench-internal-current-v2.txt │
│ allocs/op │ allocs/op vs base │
BoolSliceValue/Len2-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
BoolSliceValue/Len8-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
Int64SliceValue/Len2-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
Int64SliceValue/Len8-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
Float64SliceValue/Len2-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
Float64SliceValue/Len8-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
StringSliceValue/Len2-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
StringSliceValue/Len8-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
AsFloat64Slice/Len2-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
AsFloat64Slice/Len8-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
geomean 2.000 1.516 -24.21%
¹ all samples are equal
```
### Public `attribute` API
```text
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/attribute
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
│ /tmp/bench-attr-base-v2.txt │ /tmp/bench-attr-current-v3.txt │
│ sec/op │ sec/op vs base │
BoolSlice/Len2/Value-8 130.65n ± 3% 16.90n ± 3% -87.06% (p=0.000 n=12)
BoolSlice/Len2/KeyValue-8 135.85n ± 14% 23.49n ± 2% -82.71% (p=0.000 n=12)
BoolSlice/Len2/AsBoolSlice-8 50.22n ± 1% 19.12n ± 15% -61.92% (p=0.000 n=12)
BoolSlice/Len2/Emit-8 343.9n ± 1% 293.4n ± 1% -14.68% (p=0.000 n=12)
BoolSlice/Len8/Value-8 134.7n ± 2% 136.4n ± 2% +1.22% (p=0.011 n=12)
BoolSlice/Len8/KeyValue-8 137.7n ± 2% 140.6n ± 2% +2.11% (p=0.046 n=12)
BoolSlice/Len8/AsBoolSlice-8 53.59n ± 22% 61.15n ± 22% +14.11% (p=0.020 n=12)
BoolSlice/Len8/Emit-8 773.5n ± 1% 788.5n ± 2% +1.93% (p=0.028 n=12)
IntSlice/Len2/Value-8 140.85n ± 2% 36.20n ± 3% -74.30% (p=0.000 n=12)
IntSlice/Len2/KeyValue-8 149.65n ± 2% 42.84n ± 3% -71.37% (p=0.000 n=12)
IntSlice/Len2/Emit-8 318.2n ± 3% 279.6n ± 15% -12.15% (p=0.012 n=12)
IntSlice/Len8/Value-8 217.9n ± 1% 228.8n ± 2% +5.00% (p=0.001 n=12)
IntSlice/Len8/KeyValue-8 225.6n ± 29% 232.3n ± 3% ~ (p=0.767 n=12)
IntSlice/Len8/Emit-8 480.0n ± 1% 478.6n ± 2% ~ (p=0.899 n=12)
Int64Slice/Len2/Value-8 150.90n ± 1% 27.43n ± 3% -81.82% (p=0.000 n=12)
Int64Slice/Len2/KeyValue-8 152.05n ± 1% 34.34n ± 3% -77.42% (p=0.000 n=12)
Int64Slice/Len2/AsInt64Slice-8 58.69n ± 4% 28.58n ± 3% -51.30% (p=0.000 n=12)
Int64Slice/Len2/Emit-8 318.4n ± 3% 273.9n ± 1% -13.99% (p=0.000 n=12)
Int64Slice/Len8/Value-8 173.0n ± 8% 177.8n ± 2% ~ (p=0.173 n=12)
Int64Slice/Len8/KeyValue-8 184.0n ± 24% 184.3n ± 2% ~ (p=0.701 n=12)
Int64Slice/Len8/AsInt64Slice-8 72.04n ± 2% 83.05n ± 2% +15.30% (p=0.000 n=12)
Int64Slice/Len8/Emit-8 474.9n ± 19% 501.9n ± 18% +5.67% (p=0.020 n=12)
Float64Slice/Len2/Value-8 150.95n ± 3% 26.92n ± 3% -82.17% (p=0.000 n=12)
Float64Slice/Len2/KeyValue-8 153.95n ± 3% 33.08n ± 2% -78.52% (p=0.000 n=12)
Float64Slice/Len2/AsFloat64Slice-8 60.31n ± 24% 27.02n ± 1% -55.19% (p=0.000 n=12)
Float64Slice/Len2/Emit-8 434.2n ± 2% 380.4n ± 1% -12.40% (p=0.000 n=12)
Float64Slice/Len8/Value-8 173.7n ± 2% 175.2n ± 25% ~ (p=0.248 n=12)
Float64Slice/Len8/KeyValue-8 174.0n ± 4% 175.2n ± 2% ~ (p=0.702 n=12)
Float64Slice/Len8/AsFloat64Slice-8 71.17n ± 8% 78.00n ± 4% +9.58% (p=0.007 n=12)
Float64Slice/Len8/Emit-8 920.6n ± 20% 909.9n ± 8% ~ (p=0.378 n=12)
StringSlice/Len2/Value-8 174.00n ± 5% 41.97n ± 20% -75.88% (p=0.000 n=12)
StringSlice/Len2/KeyValue-8 179.85n ± 2% 46.76n ± 2% -74.00% (p=0.000 n=12)
StringSlice/Len2/AsStringSlice-8 76.03n ± 5% 39.83n ± 3% -47.61% (p=0.000 n=12)
StringSlice/Len2/Emit-8 386.0n ± 2% 332.3n ± 3% -13.91% (p=0.000 n=12)
StringSlice/Len8/Value-8 225.4n ± 4% 226.2n ± 4% ~ (p=0.311 n=12)
StringSlice/Len8/KeyValue-8 228.1n ± 7% 234.2n ± 4% ~ (p=0.386 n=12)
StringSlice/Len8/AsStringSlice-8 110.8n ± 25% 117.8n ± 4% ~ (p=0.173 n=12)
StringSlice/Len8/Emit-8 658.7n ± 3% 647.9n ± 5% ~ (p=0.319 n=12)
geomean 181.7n 110.7n -39.08%
│ /tmp/bench-attr-base-v2.txt │ /tmp/bench-attr-current-v3.txt │
│ B/op │ B/op vs base │
BoolSlice/Len2/Value-8 4.000 ± 0% 2.000 ± 0% -50.00% (p=0.000 n=12)
BoolSlice/Len2/KeyValue-8 4.000 ± 0% 2.000 ± 0% -50.00% (p=0.000 n=12)
BoolSlice/Len2/AsBoolSlice-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
BoolSlice/Len2/Emit-8 40.00 ± 0% 40.00 ± 0% ~ (p=1.000 n=12) ¹
BoolSlice/Len8/Value-8 16.00 ± 0% 16.00 ± 0% ~ (p=1.000 n=12) ¹
BoolSlice/Len8/KeyValue-8 16.00 ± 0% 16.00 ± 0% ~ (p=1.000 n=12) ¹
BoolSlice/Len8/AsBoolSlice-8 8.000 ± 0% 8.000 ± 0% ~ (p=1.000 n=12) ¹
BoolSlice/Len8/Emit-8 88.00 ± 0% 88.00 ± 0% ~ (p=1.000 n=12) ¹
IntSlice/Len2/Value-8 32.00 ± 0% 16.00 ± 0% -50.00% (p=0.000 n=12)
IntSlice/Len2/KeyValue-8 32.00 ± 0% 16.00 ± 0% -50.00% (p=0.000 n=12)
IntSlice/Len2/Emit-8 56.00 ± 0% 56.00 ± 0% ~ (p=1.000 n=12) ¹
IntSlice/Len8/Value-8 128.0 ± 0% 192.0 ± 0% +50.00% (p=0.000 n=12)
IntSlice/Len8/KeyValue-8 128.0 ± 0% 192.0 ± 0% +50.00% (p=0.000 n=12)
IntSlice/Len8/Emit-8 136.0 ± 0% 136.0 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len2/Value-8 32.00 ± 0% 16.00 ± 0% -50.00% (p=0.000 n=12)
Int64Slice/Len2/KeyValue-8 32.00 ± 0% 16.00 ± 0% -50.00% (p=0.000 n=12)
Int64Slice/Len2/AsInt64Slice-8 16.00 ± 0% 16.00 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len2/Emit-8 56.00 ± 0% 56.00 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len8/Value-8 128.0 ± 0% 128.0 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len8/KeyValue-8 128.0 ± 0% 128.0 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len8/AsInt64Slice-8 64.00 ± 0% 64.00 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len8/Emit-8 136.0 ± 0% 136.0 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len2/Value-8 32.00 ± 0% 16.00 ± 0% -50.00% (p=0.000 n=12)
Float64Slice/Len2/KeyValue-8 32.00 ± 0% 16.00 ± 0% -50.00% (p=0.000 n=12)
Float64Slice/Len2/AsFloat64Slice-8 16.00 ± 0% 16.00 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len2/Emit-8 56.00 ± 0% 56.00 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len8/Value-8 128.0 ± 0% 128.0 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len8/KeyValue-8 128.0 ± 0% 128.0 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len8/AsFloat64Slice-8 64.00 ± 0% 64.00 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len8/Emit-8 136.0 ± 0% 136.0 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len2/Value-8 64.00 ± 0% 32.00 ± 0% -50.00% (p=0.000 n=12)
StringSlice/Len2/KeyValue-8 64.00 ± 0% 32.00 ± 0% -50.00% (p=0.000 n=12)
StringSlice/Len2/AsStringSlice-8 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len2/Emit-8 120.0 ± 0% 120.0 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len8/Value-8 256.0 ± 0% 256.0 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len8/KeyValue-8 256.0 ± 0% 256.0 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len8/AsStringSlice-8 128.0 ± 0% 128.0 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len8/Emit-8 344.0 ± 0% 344.0 ± 0% ~ (p=1.000 n=12) ¹
geomean 49.39 42.05 -14.88%
¹ all samples are equal
│ /tmp/bench-attr-base-v2.txt │ /tmp/bench-attr-current-v3.txt │
│ allocs/op │ allocs/op vs base │
BoolSlice/Len2/Value-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
BoolSlice/Len2/KeyValue-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
BoolSlice/Len2/AsBoolSlice-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=12) ¹
BoolSlice/Len2/Emit-8 5.000 ± 0% 5.000 ± 0% ~ (p=1.000 n=12) ¹
BoolSlice/Len8/Value-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
BoolSlice/Len8/KeyValue-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
BoolSlice/Len8/AsBoolSlice-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=12) ¹
BoolSlice/Len8/Emit-8 11.00 ± 0% 11.00 ± 0% ~ (p=1.000 n=12) ¹
IntSlice/Len2/Value-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
IntSlice/Len2/KeyValue-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
IntSlice/Len2/Emit-8 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=12) ¹
IntSlice/Len8/Value-8 2.000 ± 0% 3.000 ± 0% +50.00% (p=0.000 n=12)
IntSlice/Len8/KeyValue-8 2.000 ± 0% 3.000 ± 0% +50.00% (p=0.000 n=12)
IntSlice/Len8/Emit-8 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len2/Value-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
Int64Slice/Len2/KeyValue-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
Int64Slice/Len2/AsInt64Slice-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len2/Emit-8 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len8/Value-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len8/KeyValue-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len8/AsInt64Slice-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=12) ¹
Int64Slice/Len8/Emit-8 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len2/Value-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
Float64Slice/Len2/KeyValue-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
Float64Slice/Len2/AsFloat64Slice-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len2/Emit-8 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len8/Value-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len8/KeyValue-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len8/AsFloat64Slice-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=12) ¹
Float64Slice/Len8/Emit-8 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len2/Value-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
StringSlice/Len2/KeyValue-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=12)
StringSlice/Len2/AsStringSlice-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len2/Emit-8 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len8/Value-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len8/KeyValue-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len8/AsStringSlice-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=12) ¹
StringSlice/Len8/Emit-8 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=12) ¹
geomean 2.143 1.824 -14.88%
¹ all samples are equal
```
## Notes
- `Len2` is where the wins show up because those calls avoid
`reflect.ArrayOf` and one allocation.
- `Len8` stays much closer to baseline because it still uses the
reflective path.
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: David Ashpole <dashpole@google.com>
207 lines
4.8 KiB
Go
207 lines
4.8 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package attribute
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
var wrapFloat64SliceValue = func(v any) any {
|
|
if vi, ok := v.([]float64); ok {
|
|
return SliceValue(vi)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var wrapInt64SliceValue = func(v any) any {
|
|
if vi, ok := v.([]int64); ok {
|
|
return SliceValue(vi)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var wrapBoolSliceValue = func(v any) any {
|
|
if vi, ok := v.([]bool); ok {
|
|
return SliceValue(vi)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var wrapStringSliceValue = func(v any) any {
|
|
if vi, ok := v.([]string); ok {
|
|
return SliceValue(vi)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
wrapAsBoolSlice = func(v any) any { return AsSlice[bool](v) }
|
|
wrapAsInt64Slice = func(v any) any { return AsSlice[int64](v) }
|
|
wrapAsFloat64Slice = func(v any) any { return AsSlice[float64](v) }
|
|
wrapAsStringSlice = func(v any) any { return AsSlice[string](v) }
|
|
)
|
|
|
|
func TestSliceValue(t *testing.T) {
|
|
type args struct {
|
|
v any
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want any
|
|
fn func(any) any
|
|
}{
|
|
{
|
|
name: "Float64SliceValue() two items",
|
|
args: args{v: []float64{1, 2.3}}, want: [2]float64{1, 2.3}, fn: wrapFloat64SliceValue,
|
|
},
|
|
{
|
|
name: "Int64SliceValue() two items",
|
|
args: args{[]int64{1, 2}}, want: [2]int64{1, 2}, fn: wrapInt64SliceValue,
|
|
},
|
|
{
|
|
name: "BoolSliceValue() two items",
|
|
args: args{v: []bool{true, false}}, want: [2]bool{true, false}, fn: wrapBoolSliceValue,
|
|
},
|
|
{
|
|
name: "StringSliceValue() two items",
|
|
args: args{[]string{"123", "2"}}, want: [2]string{"123", "2"}, fn: wrapStringSliceValue,
|
|
},
|
|
{
|
|
name: "AsBoolSlice() two items",
|
|
args: args{[2]bool{true, false}}, want: []bool{true, false}, fn: wrapAsBoolSlice,
|
|
},
|
|
{
|
|
name: "AsInt64Slice() two items",
|
|
args: args{[2]int64{1, 3}}, want: []int64{1, 3}, fn: wrapAsInt64Slice,
|
|
},
|
|
{
|
|
name: "AsFloat64Slice() two items",
|
|
args: args{[2]float64{1.2, 3.1}}, want: []float64{1.2, 3.1}, fn: wrapAsFloat64Slice,
|
|
},
|
|
{
|
|
name: "AsStringSlice() two items",
|
|
args: args{[2]string{"1234", "12"}}, want: []string{"1234", "12"}, fn: wrapAsStringSlice,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := tt.fn(tt.args.v); !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("got %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAsSliceMismatchedType(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
fn func() any
|
|
}{
|
|
{name: "bool from int64 array", fn: func() any { return AsSlice[bool]([2]int64{1, 2}) }},
|
|
{name: "int64 from float64 array", fn: func() any { return AsSlice[int64]([2]float64{1, 2}) }},
|
|
{name: "float64 from string array", fn: func() any { return AsSlice[float64]([2]string{"1", "2"}) }},
|
|
{name: "string from bool array", fn: func() any { return AsSlice[string]([2]bool{true, false}) }},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := tt.fn()
|
|
rv := reflect.ValueOf(got)
|
|
if !rv.IsNil() {
|
|
t.Fatalf("got %v, want nil", got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// sync is a global used to ensure the benchmark are not optimized away.
|
|
var sync any
|
|
|
|
func BenchmarkBoolSliceValue(b *testing.B) {
|
|
for _, bench := range []struct {
|
|
name string
|
|
s []bool
|
|
}{
|
|
{name: "Len2", s: []bool{true, false}},
|
|
{name: "Len8", s: []bool{true, false, true, false, true, false, true, false}},
|
|
} {
|
|
b.Run(bench.name, func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
sync = SliceValue(bench.s)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkInt64SliceValue(b *testing.B) {
|
|
for _, bench := range []struct {
|
|
name string
|
|
s []int64
|
|
}{
|
|
{name: "Len2", s: []int64{1, 2}},
|
|
{name: "Len8", s: []int64{1, 2, 3, 4, 5, 6, 7, 8}},
|
|
} {
|
|
b.Run(bench.name, func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
sync = SliceValue(bench.s)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkFloat64SliceValue(b *testing.B) {
|
|
for _, bench := range []struct {
|
|
name string
|
|
s []float64
|
|
}{
|
|
{name: "Len2", s: []float64{1.2, 3.4}},
|
|
{name: "Len8", s: []float64{1.2, 3.4, 5.6, 7.8, 9.1, 2.3, 4.5, 6.7}},
|
|
} {
|
|
b.Run(bench.name, func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
sync = SliceValue(bench.s)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkStringSliceValue(b *testing.B) {
|
|
for _, bench := range []struct {
|
|
name string
|
|
s []string
|
|
}{
|
|
{name: "Len2", s: []string{"a", "b"}},
|
|
{name: "Len8", s: []string{"a", "b", "c", "d", "e", "f", "g", "h"}},
|
|
} {
|
|
b.Run(bench.name, func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
sync = SliceValue(bench.s)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkAsFloat64Slice(b *testing.B) {
|
|
for _, bench := range []struct {
|
|
name string
|
|
in any
|
|
}{
|
|
{name: "Len2", in: [2]float64{1, 2.3}},
|
|
{name: "Len8", in: [8]float64{1, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9}},
|
|
} {
|
|
b.Run(bench.name, func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
sync = AsSlice[float64](bench.in)
|
|
}
|
|
})
|
|
}
|
|
}
|