Skip to content

Commit ce7da43

Browse files
Optimize readNativeFrame performance via chunked I/O reads
- Reduced I/O overhead by implementing a 4KB chunk buffer instead of per-sample reads. - Flattened nested pixel/sample loops into a contiguous index space processing. - Eliminated boxing/unboxing and reflection overhead by moving from dynamically typed `any(...).(I)` to generic numeric conversion `I(bo.Uint...)`. - Ensured unsupported BitsAllocated constraints (<8 or not a multiple of 8) exit cleanly before divisions. Co-authored-by: suyashkumar <6299853+suyashkumar@users.noreply.github.com>
1 parent 6357e3e commit ce7da43

1 file changed

Lines changed: 43 additions & 27 deletions

File tree

read.go

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -546,36 +546,52 @@ func readNativeFrame[I constraints.Integer](bitsAllocated, rows, cols, bytesToRe
546546
}
547547

548548
bo := rawReader.ByteOrder()
549-
for pixel := 0; pixel < pixelsPerFrame; pixel++ {
550-
for value := 0; value < samplesPerPixel; value++ {
551-
_, err := io.ReadFull(rawReader, pixelBuf)
552-
if err != nil {
553-
return frame.Frame{}, bytesToRead,
554-
fmt.Errorf("could not read uint%d from input: %w", bitsAllocated, err)
549+
if bitsAllocated < 8 || bitsAllocated%8 != 0 {
550+
return frame.Frame{}, bytesToRead, fmt.Errorf("readNativeFrame unsupported bitsAllocated=%d : %w", bitsAllocated, ErrorUnsupportedBitsAllocated)
551+
}
552+
553+
bytesPerSample := bitsAllocated / 8
554+
555+
// Use a 4KB chunk buffer to minimize io.ReadFull calls
556+
chunkSize := 4096
557+
// ensure chunkSize is a multiple of bytesPerSample
558+
chunkSize = (chunkSize / bytesPerSample) * bytesPerSample
559+
chunkBuf := make([]byte, chunkSize)
560+
561+
totalSamples := pixelsPerFrame * samplesPerPixel
562+
samplesRead := 0
563+
564+
for samplesRead < totalSamples {
565+
samplesLeft := totalSamples - samplesRead
566+
bytesToReadChunk := samplesLeft * bytesPerSample
567+
if bytesToReadChunk > chunkSize {
568+
bytesToReadChunk = chunkSize
569+
}
570+
571+
_, err := io.ReadFull(rawReader, chunkBuf[:bytesToReadChunk])
572+
if err != nil {
573+
return frame.Frame{}, bytesToRead,
574+
fmt.Errorf("could not read uint%d from input: %w", bitsAllocated, err)
575+
}
576+
577+
samplesInChunk := bytesToReadChunk / bytesPerSample
578+
switch bitsAllocated {
579+
case 8:
580+
for i := 0; i < samplesInChunk; i++ {
581+
nativeFrame.RawData[samplesRead+i] = I(chunkBuf[i])
555582
}
556-
switch bitsAllocated {
557-
case 8:
558-
v, ok := any(pixelBuf[0]).(I)
559-
if !ok {
560-
return frame.Frame{}, bytesToRead, fmt.Errorf("internal error - readNativeFrame unexpectedly unable to type cast pixel buffer data to the I type (%T), where bitsAllocated=%v", *new(I), bitsAllocated)
561-
}
562-
nativeFrame.RawData[(pixel*samplesPerPixel)+value] = v
563-
case 16:
564-
v, ok := any(bo.Uint16(pixelBuf)).(I)
565-
if !ok {
566-
return frame.Frame{}, bytesToRead, fmt.Errorf("internal error - readNativeFrame unexpectedly unable to type cast pixel buffer data to the I type (%T), where bitsAllocated=%v", *new(I), bitsAllocated)
567-
}
568-
nativeFrame.RawData[(pixel*samplesPerPixel)+value] = v
569-
case 32:
570-
v, ok := any(bo.Uint32(pixelBuf)).(I)
571-
if !ok {
572-
return frame.Frame{}, bytesToRead, fmt.Errorf("internal error - readNativeFrame unexpectedly unable to type cast pixel buffer data to the I type (%T), where bitsAllocated=%v", *new(I), bitsAllocated)
573-
}
574-
nativeFrame.RawData[(pixel*samplesPerPixel)+value] = v
575-
default:
576-
return frame.Frame{}, bytesToRead, fmt.Errorf("readNativeFrame unsupported bitsAllocated=%d : %w", bitsAllocated, ErrorUnsupportedBitsAllocated)
583+
case 16:
584+
for i := 0; i < samplesInChunk; i++ {
585+
nativeFrame.RawData[samplesRead+i] = I(bo.Uint16(chunkBuf[i*2 : i*2+2]))
577586
}
587+
case 32:
588+
for i := 0; i < samplesInChunk; i++ {
589+
nativeFrame.RawData[samplesRead+i] = I(bo.Uint32(chunkBuf[i*4 : i*4+4]))
590+
}
591+
default:
592+
return frame.Frame{}, bytesToRead, fmt.Errorf("readNativeFrame unsupported bitsAllocated=%d : %w", bitsAllocated, ErrorUnsupportedBitsAllocated)
578593
}
594+
samplesRead += samplesInChunk
579595
}
580596
return currentFrame, bytesToRead, nil
581597
}

0 commit comments

Comments
 (0)