commit 10025629ef7bfecd4b0a0ed827d5390fddeac520
parent 7981a03eda14412c644a71b4cd9820fb5d097584
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Tue, 26 Oct 2021 21:53:09 +0800
nativeaudio: use built-in helpers at the cgo boundary
The performance gain is negligible so we shall prefer correctness.
BEFORE
goos: windows
goarch: amd64
pkg: git.sr.ht/~jackmordaunt/nativeaudio/internal/test
cpu: AMD Ryzen 5 3600 6-Core Processor
BenchmarkDecode/native-load-12 165 7272938 ns/op 88 B/op 3 allocs/op
BenchmarkDecode/native-decode-12 164 7075614 ns/op 72 B/op 2 allocs/op
BenchmarkDecode/ffmpeg-load-12 19 61365137 ns/op 2262458 B/op 1423 allocs/op
AFTER
goos: windows
goarch: amd64
pkg: git.sr.ht/~jackmordaunt/nativeaudio/internal/test
cpu: AMD Ryzen 5 3600 6-Core Processor
BenchmarkDecode/native-load-12 165 7371150 ns/op 606245 B/op 3 allocs/op
BenchmarkDecode/native-decode-12 159 7471619 ns/op 606250 B/op 3 allocs/op
BenchmarkDecode/ffmpeg-load-12 19 61339426 ns/op 2262489 B/op 1423 allocs/op
Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Diffstat:
1 file changed, 5 insertions(+), 29 deletions(-)
diff --git a/audio_windows.go b/audio_windows.go
@@ -15,7 +15,6 @@ import "C"
import (
"errors"
"fmt"
- "runtime"
"strings"
"unsafe"
)
@@ -62,10 +61,8 @@ func load(path string) (uncompressed []byte, format Format, err error) {
defer C.ErrorFree(r.Err)
return nil, Format{}, collectErrors(r.Err)
}
- uncompressed = goBytes(unsafe.Pointer(r.Uncompressed.Data), int(r.Uncompressed.Len))
- runtime.SetFinalizer(&uncompressed, func(_ *[]byte) {
- C.BufferFree(r.Uncompressed)
- })
+ defer C.BufferFree(r.Uncompressed)
+ uncompressed = C.GoBytes(unsafe.Pointer(r.Uncompressed.Data), C.int(r.Uncompressed.Len))
format = Format{
SampleRate: int(r.Format.SampleRate),
BitDepth: int(r.Format.BitDepth),
@@ -79,16 +76,13 @@ func load(path string) (uncompressed []byte, format Format, err error) {
//
// uncompressed is a read-only slice backed by a C buffer. Do not mutate.
func decode(compressed []byte) (uncompressed []byte, format Format, err error) {
- defer runtime.KeepAlive(compressed)
- r := C.Decode(cBytes(compressed))
+ r := C.Decode((*C.uchar)(C.CBytes(compressed)), C.uint(len(compressed)))
if r.Err != nil && r.Err.Str != nil {
defer C.ErrorFree(r.Err)
return nil, format, collectErrors(r.Err)
}
- uncompressed = goBytes(unsafe.Pointer(r.Uncompressed.Data), int(r.Uncompressed.Len))
- runtime.SetFinalizer(&uncompressed, func(_ *[]byte) {
- C.BufferFree(r.Uncompressed)
- })
+ defer C.BufferFree(r.Uncompressed)
+ uncompressed = C.GoBytes(unsafe.Pointer(r.Uncompressed.Data), C.int(r.Uncompressed.Len))
format = Format{
Channels: int(r.Format.Channels),
BitDepth: int(r.Format.BitDepth),
@@ -97,24 +91,6 @@ func decode(compressed []byte) (uncompressed []byte, format Format, err error) {
return uncompressed, format, nil
}
-// goBytes returns a slice backed by a C byte array.
-//
-// [1 << 30] means assume backing array is 1GB, and then slice into it
-// with length.
-//
-// If the data is larger than 1GB, allocate more memory.
-func goBytes(ptr unsafe.Pointer, length int) []byte {
- if length > 1<<30 {
- return C.GoBytes(ptr, C.int(length))
- }
- return (*[1 << 30]byte)(ptr)[:length:length]
-}
-
-// cBytes returns a dynamic C byte array backed by a Go slice.
-func cBytes(by []byte) (*C.uchar, C.uint) {
- return (*C.uchar)(unsafe.Pointer(&by[0])), C.uint(len(by))
-}
-
// collectErrors unwraps all the errors in the chain and coalesces them
// into a single Go error.
func collectErrors(err *C.Error) error {