WalzoneInterview Prep
📞 Interviewing soon? Practice with a realistic AI mock phone interview — it calls you, then scores you. First 15 min FREE →

Go · Guru · question 98 of 100

Can you detail a scenario where you had to use Go’s unsafe package for performance optimization?

📕 Buy this interview preparation book: 100 Go questions & answers — PDF + EPUB for $5

In this scenario, I had to optimize the performance of an application that frequently converted byte slices to float64 slices and vice versa. The application’s primary function was to consume data from an external source, perform real-time numerical calculations, and send the results to another system for further analysis. Given the application’s nature, it was crucial to minimize the latency and maximize the throughput.

The typical and safe way to convert a byte slice to a float64 slice is to use binary.Read with a bytes.Buffer:

import (
    "bytes"
    "encoding/binary"
    "io"
)

func bytesToFloat64s(b []byte) ([]float64, error) {
    var f []float64

    buf := bytes.NewReader(b)
    for {
        var temp float64
        err := binary.Read(buf, binary.LittleEndian, &temp)
        if err == io.EOF {
            break
        } else if err != nil {
            return nil, err
        }
        f = append(f, temp)
    }

    return f, nil
}

While this approach works, it’s not very efficient due to the numerous function calls, error checks, and internal memory allocations.

To optimize the performance, we can use the ‘unsafe‘ package to directly cast the byte slice to a float64 slice, effectively reducing the overhead:

import (
    "reflect"
    "unsafe"
)

func bytesToFloat64s(b []byte) []float64 {
    if len(b) % 8 != 0 {
        panic("byte slice length is not divisible by 8")
    }
    
    var f []float64
    headerF := *(*reflect.SliceHeader)(unsafe.Pointer(&f))
    
    headerF.Data = (*reflect.SliceHeader)(unsafe.Pointer(&b)).Data
    headerF.Len = len(b) / 8
    headerF.Cap = len(b) / 8
    
    return *(*[]float64)(unsafe.Pointer(&headerF))
}

In this optimized version, we utilize the unsafe.Pointer to circumvent Go’s type safety checks. We employ reflect.SliceHeader to directly access and modify the underlying slice metadata. The resulting function is more efficient, but it comes with potential risks associated with using the unsafe package, such as memory corruption, data races, or breaking compatibility with future Go releases.

To ensure that the major benefits of this optimization are sustained, it’s important to exercise caution when working with the unsafe package. Properly test and validate the code to avoid unexpected behavior.

Here is a small chart illustrating the latency reduction when using the unsafe package for the byte to float64 slice conversion:

As the chart shows, using the unsafe package for this specific optimization results in a significant reduction in latency, especially as the number of elements in the byte slice increases.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic Go interview — then scores it.
📞 Practice Go — free 15 min
📕 Buy this interview preparation book: 100 Go questions & answers — PDF + EPUB for $5

All 100 Go questions · All topics