Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions packer/copy_uints.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Byte->word conversion of the little-endian streams.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file named copy_uints.go. Do you think it's a good name for this particular file? I'd personally put it somewhere to utils package and named copy_utils.go

//
// On little-endian hosts the in-memory representation of []uint32/[]uint64 is

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please clean up the comment. That's a typical huge claude comment explaining trivial things. And there a lot of questionable numbers and statements as well.

// exactly the on-disk little-endian stream, so the conversion is a bulk copy
// (runtime.memmove) instead of an element-wise decode loop. The loop tops out
// at ~4.5 GiB/s regardless of size (instruction-bound); memmove runs at
// memory/cache bandwidth (30-70 GiB/s), see copy_uints_test.go benchmarks.
// For the smallest inputs (streams of a couple of words: offsets arrays of
// blocks dominated by one heavy token, fraction-tail blocks) the memmove call
// overhead loses to a plain loop, so those stay element-wise.
//
// Big-endian hosts use the element-wise fallback in copy_uints_be.go.

//go:build 386 || amd64 || arm || arm64 || loong64 || mipsle || mips64le || ppc64le || riscv64 || wasm

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have odd feelings about introducing build tags for this small optimization. Initially I did this, it just didn't get into main branch: 22a3e36#diff-aab3afc5841441065dc926e0dccdfca3010a8eb081ccc5c04bb394f902bde1d3R17

I though it's easier to just check if host is LE and then use if branch.

The main problem I see with the proposed solution is I don't know much about these platforms and build tags look like overkill for this kind of micro-optimization. I also looked through arrow-go library, haven't found any build tags, but endianess field is there in the schema.

What are you thoughts?


package packer

import (
"encoding/binary"
"fmt"
"unsafe"
)

// smallCopyWords: at or below this the plain loop beats the memmove call
// overhead. Measured crossover (BenchmarkSmallCopyCrossover, Cascade Lake):
// the loop wins at 1-2 words, ~ties at 3, memmove wins from 4 on (-15%)
// and widens quickly (-48% at 8 words).
const smallCopyWords = 3

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this smallCopyWords, I'd generally expect we pass large blocks (128 bytes or more) through this function.


// copyAsUints32 reinterprets dst as bytes and bulk-copies src into it.
// Panics if len(src) is not a multiple of the word size — same fail-fast

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for comments, please simplify.

// behavior on a corrupted stream as the element-wise decode had; callers
// validate the length beforehand.
func copyAsUints32(src []byte, dst []uint32) []uint32 {
if len(src)%sizeOfUint32 != 0 {
panic(fmt.Sprintf("packer: ragged uint32 stream: %d bytes", len(src)))
}
n := len(src) / sizeOfUint32
if cap(dst) < n {
dst = make([]uint32, n)
}
dst = dst[:n]
if n == 0 {
return dst
}
if n <= smallCopyWords {
for i := range dst {
dst[i] = binary.LittleEndian.Uint32(src[i*sizeOfUint32:])
}
return dst
}
copy(unsafe.Slice((*byte)(unsafe.Pointer(&dst[0])), n*sizeOfUint32), src)
return dst
}

// copyAsUints64 reinterprets dst as bytes and bulk-copies src into it.
// Panics if len(src) is not a multiple of the word size (see copyAsUints32).
func copyAsUints64(src []byte, dst []uint64) []uint64 {
if len(src)%8 != 0 {
panic(fmt.Sprintf("packer: ragged uint64 stream: %d bytes", len(src)))
}
n := len(src) / 8

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

len(src) / sizeOfUint64?

if cap(dst) < n {
dst = make([]uint64, n)
}
dst = dst[:n]
if n == 0 {
return dst
}
if n <= smallCopyWords {
for i := range dst {
dst[i] = binary.LittleEndian.Uint64(src[i*8:])
}
return dst
}
copy(unsafe.Slice((*byte)(unsafe.Pointer(&dst[0])), n*8), src)
return dst
}
43 changes: 43 additions & 0 deletions packer/copy_uints_be.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Big-endian fallback: element-wise little-endian decode with the same
// contract as the bulk-copy variant (including the ragged-length panic).
// No release target or CI runs big-endian, so this path is not exercised
// by any automated build.

//go:build !(386 || amd64 || arm || arm64 || loong64 || mipsle || mips64le || ppc64le || riscv64 || wasm)

package packer

import (
"encoding/binary"
"fmt"
)

func copyAsUints32(src []byte, dst []uint32) []uint32 {
if len(src)%sizeOfUint32 != 0 {
panic(fmt.Sprintf("packer: ragged uint32 stream: %d bytes", len(src)))
}
n := len(src) / sizeOfUint32
if cap(dst) < n {
dst = make([]uint32, n)
}
dst = dst[:n]
for i := range dst {
dst[i] = binary.LittleEndian.Uint32(src[i*sizeOfUint32:])
}
return dst
}

func copyAsUints64(src []byte, dst []uint64) []uint64 {
if len(src)%8 != 0 {
panic(fmt.Sprintf("packer: ragged uint64 stream: %d bytes", len(src)))
}
n := len(src) / 8
if cap(dst) < n {
dst = make([]uint64, n)
}
dst = dst[:n]
for i := range dst {
dst[i] = binary.LittleEndian.Uint64(src[i*8:])
}
return dst
}
Loading