Reputation: 5767
I'm trying to convert 10 millions of int to Hex, then, padding it with 0's in order to get a 4 characters string which represents the Hex number.
So far, I tried the following:
var hexNumber string
for idx := O; idx < 10000000; idx++ {
hexNumber = fmt.Sprintf("%04x", idx)
// Do some stuff ....
}
But fmt.Sprintf
is not very efficient. How can I achieve this in a efficient way?
Solution: It turns out that @peterSO strconv.AppendInt
solution is way faster.
package bench
import (
"fmt"
"strconv"
"strings"
"testing"
)
var stringHex [16]string
var runesHex [16]rune
func init() {
stringHex = [16]string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}
runesHex = [16]rune{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}
}
func intToHex1(intNumber int) string {
hexNumber := []rune("0000")
for i, j := int(0), uint(12); i < 4; i, j = i+1, j-4 {
hexNumber[i] = runesHex[(intNumber>>j)&0x0f]
}
return string(hexNumber)
}
func intToHex2(intNumber int) string {
hexNumber := "0000"
for i, j := int(0), uint(12); i < 4; i, j = i+1, j-4 {
hexNumber = hexNumber[:i] + stringHex[(intNumber>>j)&0x0f] + hexNumber[i+1:]
}
return hexNumber
}
func BenchmarkFmtSprintf(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
hexNumber := fmt.Sprintf("%04x", n)
_ = hexNumber
}
}
func BenchmarkStrconvFormatInt(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
retStr := strings.Repeat("0", 4) + strconv.FormatInt(int64(n), 16)
hexNumber := retStr[(len(retStr) - 4):]
_ = hexNumber
}
}
func BenchmarkAppend(b *testing.B) {
b.ReportAllocs()
buf := []byte{'0', '0', '0', '0', 4 + 16: 0}
for n := 0; n < b.N; n++ {
buf = strconv.AppendInt(buf[:4], int64(n), 16)
hexNumber := string(buf[len(buf)-4:])
_ = hexNumber
}
}
func BenchmarkIntToHex1(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
hexNumber := intToHex1(n)
_ = hexNumber
}
}
func BenchmarkIntToHex2(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
hexNumber := intToHex2(n)
_ = hexNumber
}
}
So the benchmark:
BenchmarkFmtSprintf-2 3000000 364 ns/op 16 B/op 2 allocs/op
BenchmarkStrconvFormatInt-2 5000000 354 ns/op 15 B/op 3 allocs/op
BenchmarkAppend-2 20000000 75.6 ns/op 0 B/op 0 allocs/op
BenchmarkIntToHex1-2 10000000 162 ns/op 8 B/op 1 allocs/op
BenchmarkIntToHex2-2 3000000 536 ns/op 16 B/op 4 allocs/op
Upvotes: 3
Views: 4476
Reputation: 420
I am not sure that this is the best way to do it, but I have a gist and playground illustrating the problem you are trying to solve and the approach I eventually took.
// hex formater with %x can lose a leading 0
// This can produce strings with an odd number of hex digits
package main
import (
"encoding/binary"
"encoding/hex"
"fmt"
)
func main() {
var foo uint16 = 0x0ABC // decimal 2748
with_pct := fmt.Sprintf("%x", foo)
fmt.Printf("With string formatter: %q\n", with_pct)
// Outputs: With string formatter: "abc"
// I hope there is a cleaner way to get an integer into a []byte
foo_bytes := make([]byte, 2)
binary.BigEndian.PutUint16(foo_bytes, foo)
with_bytes := hex.EncodeToString(foo_bytes)
fmt.Printf("With hex encoding: %q\n", with_bytes)
// Outputs: With hex encoding: "0abc"
}
I am not particularly happy with this approach. I have to manually set the length of the byte array based on the size of the int, as Go's len()
function isn't like C's size_of
operator.
This does work more nicely when trying to get a hex representation of an non-negative big.Int, as .Bytes()
gives us the big-endian byte array.
Upvotes: 0
Reputation: 166596
strconv.AppendUint
appears to be faster than fmt.Sprintf
. For example,
hex_test.go
:
package main
import (
"fmt"
"strconv"
"testing"
)
func BenchmarkFmtSprintf(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
hexNumber := fmt.Sprintf("%04x", n&0xFFFF)
_ = hexNumber
}
}
func BenchmarkAppend(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
buf := []byte{'0', '0', '0', 3 + 4: 0}
buf = strconv.AppendUint(buf[:3], uint64(n)&0xFFFF, 16)
hexNumber := string(buf[len(buf)-4:])
_ = hexNumber // Do some stuff ....
}
}
Output:
$ go test -bench=. hex_test.go
BenchmarkSprintf-4 10000000 116 ns/op 16 B/op 1 allocs/op
BenchmarkAppend-4 100000000 19.2 ns/op 0 B/op 0 allocs/op
Upvotes: 3
Reputation: 9623
You should be able to use strconv, then just pad manually. This probably does less work.
strconv.FormatInt(idx,16)
Upvotes: -1