Efficient way to convert an int to Hex string and then pad it with 0's in Golang?

Issue

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

Solution

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

Answered By – peterSO

Answer Checked By – Senaida (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.