Skip to content

Commit 778691a

Browse files
authored
Performance tweaks (#82)
- Speed up parsing and formatting
1 parent dd0c83a commit 778691a

14 files changed

+489
-293
lines changed

Makefile

+56-20
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,75 @@
11
.PHONY: all
2-
all: test test-debug build-linux lint
2+
all: test-all build-linux lint
3+
4+
5+
.PHONY: test-all
6+
test-all: test test-debug test-32
37

48
.PHONY: test
59
test:
6-
go test
10+
go test $(GOTESTFLAGS)
711

812
.PHONY: test-debug
913
test-debug:
10-
go test -tags=decimal_debug
14+
go test $(GOTESTFLAGS) -tags=decimal_debug
15+
16+
.PHONY: test-32
17+
test-32:
18+
$(DOCKERRUN) -e GOARCH=arm golang:1.23.0 go test $(GOTESTFLAGS)
1119

1220
.PHONY: build-linux
1321
build-linux:
14-
GOOS=linux $(MAKE) build build-32bit
22+
GOOS=linux $(MAKE) build
1523

24+
# Prime Go caches for docker golangci-lint.
1625
.PHONY: build
17-
build-64bit:
18-
go test -c -o decimal.$@.test . && rm -f decimal.$@.test
19-
go test -c -o decimal.$@.debug.test -tags=decimal_debug . && rm -f decimal.$@.debug.test
26+
build: build-64-bit build-32-bit
27+
28+
.PHONY: build-32-bit build-64-bit
29+
build-32-bit: decimal.32.release.test decimal.32.debug.test
30+
build-64-bit: decimal.64.release.test decimal.64.debug.test
31+
32+
GOARCH.32=arm
33+
GOARCH.64=
34+
35+
.INTERMEDIATE: decimal.32.release.test decimal.64.release.test
36+
decimal.%.release.test:
37+
GOARCH=$(GOARCH.$*) go test -c -o $@ $(GOTESTFLAGS) .
38+
39+
.INTERMEDIATE: decimal.32.debug.test decimal.64.debug.test
40+
decimal.%.debug.test:
41+
GOARCH=$(GOARCH.$*) go test -c -o $@ -tags=decimal_debug $(GOTESTFLAGS) .
42+
43+
DOCKERRUN = docker run --rm \
44+
-w /app \
45+
-v $(PWD):/app \
46+
-v `go env GOCACHE`:/root/.cache/go-build \
47+
-v `go env GOMODCACHE`:/go/pkg/mod
2048

21-
.PHONY: build-32bit
22-
build-32bit:
23-
GOARCH=arm go test -c -o decimal.$@.test . && rm -f decimal.$@.test
24-
GOARCH=arm go test -c -o decimal.$@.debug.test -tags=decimal_debug . && rm -f decimal.$@.debug.test
2549

2650
# Dependency on build-linux primes Go caches.
2751
.PHONY: lint
2852
lint: build-linux
29-
docker run --rm \
30-
-w /app \
31-
-v $(PWD):/app \
32-
-v `go env GOCACHE`:/root/.cache/go-build \
33-
-v `go env GOMODCACHE`:/go/pkg/mod \
34-
golangci/golangci-lint:v1.60.1-alpine \
35-
golangci-lint run
53+
$(DOCKERRUN) golangci/golangci-lint:v1.60.1-alpine golangci-lint run
3654

3755
.PHONY: profile
38-
profile:
39-
go test -cpuprofile cpu.prof -count=10 && go tool pprof -http=:8080 cpu.prof
56+
profile: cpu.prof
57+
go tool pprof -http=:8080 $<
58+
59+
.INTERMEDIATE: cpu.prof
60+
cpu.prof:
61+
go test -cpuprofile $@ -count=10 $(GOTESTFLAGS)
62+
63+
.PHONY: bench
64+
bench: bench.txt
65+
cat $<
66+
67+
bench-stat: bench.stat
68+
cat $<
69+
70+
bench.stat: bench.txt
71+
[ -f bench.old ] || git show @:$< > bench.old || (rm -f $@; false)
72+
benchstat bench.old $< > $@ || (rm -f $@; false)
73+
74+
bench.txt: test
75+
go test -run=^$$ -bench=. -benchmem -count=10 $(GOTESTFLAGS) > $@ || (rm -f $@; false)

decimal64.go

+63-41
Original file line numberDiff line numberDiff line change
@@ -105,32 +105,59 @@ func (ctx Rounding) round(significand uint64, rndStatus discardedDigit) uint64 {
105105
return significand
106106
}
107107

108-
func signalNaN64() {
109-
panic("sNaN64")
110-
}
111-
112-
var small64s = [...]Decimal64{
113-
new64FromInt64(-10),
114-
new64FromInt64(-9),
115-
new64FromInt64(-8),
116-
new64FromInt64(-7),
117-
new64FromInt64(-6),
118-
new64FromInt64(-5),
119-
new64FromInt64(-4),
120-
new64FromInt64(-3),
121-
new64FromInt64(-2),
122-
NegOne64,
123-
Zero64,
124-
One64,
125-
new64FromInt64(2),
126-
new64FromInt64(3),
127-
new64FromInt64(4),
128-
new64FromInt64(5),
129-
new64FromInt64(6),
130-
new64FromInt64(7),
131-
new64FromInt64(8),
132-
new64FromInt64(9),
133-
new64FromInt64(10),
108+
var ErrNaN64 error = Error("sNaN64")
109+
110+
var small64s = []Decimal64{
111+
newFromPartsRaw(1, -14, 1*decimal64Base),
112+
113+
newFromPartsRaw(1, -15, 9*decimal64Base),
114+
newFromPartsRaw(1, -15, 8*decimal64Base),
115+
newFromPartsRaw(1, -15, 7*decimal64Base),
116+
newFromPartsRaw(1, -15, 6*decimal64Base),
117+
newFromPartsRaw(1, -15, 5*decimal64Base),
118+
newFromPartsRaw(1, -15, 4*decimal64Base),
119+
newFromPartsRaw(1, -15, 3*decimal64Base),
120+
newFromPartsRaw(1, -15, 2*decimal64Base),
121+
newFromPartsRaw(1, -15, 1*decimal64Base),
122+
123+
// TODO: Decimal64{}?
124+
newFromPartsRaw(0, 0, 0),
125+
126+
newFromPartsRaw(0, -15, 1*decimal64Base),
127+
newFromPartsRaw(0, -15, 2*decimal64Base),
128+
newFromPartsRaw(0, -15, 3*decimal64Base),
129+
newFromPartsRaw(0, -15, 4*decimal64Base),
130+
newFromPartsRaw(0, -15, 5*decimal64Base),
131+
newFromPartsRaw(0, -15, 6*decimal64Base),
132+
newFromPartsRaw(0, -15, 7*decimal64Base),
133+
newFromPartsRaw(0, -15, 8*decimal64Base),
134+
newFromPartsRaw(0, -15, 9*decimal64Base),
135+
136+
newFromPartsRaw(0, -14, 1*decimal64Base),
137+
}
138+
139+
var small64Strings = map[Decimal64]string{
140+
small64s[0]: "-10",
141+
small64s[1]: "-9",
142+
small64s[2]: "-8",
143+
small64s[3]: "-7",
144+
small64s[4]: "-6",
145+
small64s[5]: "-5",
146+
small64s[6]: "-4",
147+
small64s[7]: "-3",
148+
small64s[8]: "-2",
149+
small64s[9]: "-1",
150+
small64s[10]: "0",
151+
small64s[11]: "1",
152+
small64s[12]: "2",
153+
small64s[13]: "3",
154+
small64s[14]: "4",
155+
small64s[15]: "5",
156+
small64s[16]: "6",
157+
small64s[17]: "7",
158+
small64s[18]: "8",
159+
small64s[19]: "9",
160+
small64s[20]: "10",
134161
}
135162

136163
// New64FromInt64 returns a new Decimal64 with the given value.
@@ -262,10 +289,12 @@ func (d Decimal64) parts() (fl flavor, sign int, exp int, significand uint64) {
262289
fl = flInf
263290
case 2:
264291
fl = flQNaN
265-
significand = d.bits & (1<<53 - 1)
292+
significand = d.bits & (1<<51 - 1)
293+
return
266294
case 3:
267295
fl = flSNaN
268-
significand = d.bits & (1<<53 - 1)
296+
significand = d.bits & (1<<51 - 1)
297+
return
269298
}
270299
case 12, 13, 14:
271300
// s 11EEeeeeeeee (100)t tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt
@@ -336,8 +365,7 @@ func (d Decimal64) Float64() float64 {
336365
case flQNaN:
337366
return math.NaN()
338367
}
339-
signalNaN64()
340-
return 0
368+
panic(ErrNaN64)
341369
}
342370

343371
// Int64 returns an int64 representation of d, clamped to [[math.MinInt64], [math.MaxInt64]].
@@ -360,8 +388,7 @@ func (d Decimal64) Int64x() (i int64, exact bool) {
360388
case flQNaN:
361389
return 0, false
362390
case flSNaN:
363-
signalNaN64()
364-
return 0, false
391+
panic(ErrNaN64)
365392
}
366393
exp, whole, frac := expWholeFrac(exp, significand)
367394
for exp > 0 && whole < math.MaxInt64/10 {
@@ -518,20 +545,15 @@ func (d Decimal64) Class() string {
518545
return "NaN"
519546
}
520547

521-
sign := "+"
522-
if dp.sign == 1 {
523-
sign = "-"
524-
}
525-
526548
switch {
527549
case dp.isInf():
528-
return sign + "Infinity"
550+
return "+Infinity-Infinity"[9*dp.sign : 9*(dp.sign+1)]
529551
case dp.isZero():
530-
return sign + "Zero"
552+
return "+Zero-Zero"[5*dp.sign : 5*(dp.sign+1)]
531553
case dp.isSubnormal():
532-
return sign + "Subnormal"
554+
return "+Subnormal-Subnormal"[10*dp.sign : 10*(dp.sign+1)]
533555
}
534-
return sign + "Normal"
556+
return "+Normal-Normal"[7*dp.sign : 7*(dp.sign+1)]
535557
}
536558

537559
// numDecimalDigits returns the magnitude (number of digits) of a uint64.

decimal64_test.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,8 @@ func TestNumDecimalDigits(t *testing.T) {
309309
}
310310

311311
func TestIsSubnormal(t *testing.T) {
312-
require := require.New(t)
313-
314-
require.Equal(true, MustParse64("0.1E-383").IsSubnormal())
315-
require.Equal(true, MustParse64("-0.1E-383").IsSubnormal())
316-
require.Equal(false, MustParse64("NaN10").IsSubnormal())
317-
require.Equal(false, New64FromInt64(42).IsSubnormal())
318-
312+
require.Equal(t, true, MustParse64("0.1E-383").IsSubnormal())
313+
require.Equal(t, true, MustParse64("-0.1E-383").IsSubnormal())
314+
require.Equal(t, false, MustParse64("NaN10").IsSubnormal())
315+
require.Equal(t, false, New64FromInt64(42).IsSubnormal())
319316
}

decimal64decParts.go

-4
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,6 @@ func (dp *decParts) isNaN() bool {
7979
return dp.fl&(flQNaN|flSNaN) != 0
8080
}
8181

82-
func (dp *decParts) isQNaN() bool {
83-
return dp.fl == flQNaN
84-
}
85-
8682
func (dp *decParts) isSNaN() bool {
8783
return dp.fl == flSNaN
8884
}

decimal64decParts_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ func TestIsNaN(t *testing.T) {
2323

2424
a.unpack(SNaN64)
2525
require.Equal(true, a.isSNaN())
26-
27-
a.unpack(QNaN64)
28-
require.Equal(true, a.isQNaN())
2926
}
3027

3128
func TestPartsSubnormal(t *testing.T) {

0 commit comments

Comments
 (0)