Last active
September 27, 2024 14:41
-
-
Save macguru/ec5769e2c6bcfd83b6a584f73d85df0f to your computer and use it in GitHub Desktop.
Using SIMD instructions inside a buffer iteration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension UnsafeMutablePointer<UInt8> { | |
/// Returns the index of the first element in the buffer that is not `0` | |
func firstIndexNotZero(size bufferSize: Int) -> Int? { | |
let stride = MemoryLayout<SIMD16<UInt8>>.stride | |
let simdSize = bufferSize / stride | |
// Iterate buffer using 16-component SIMD vectors | |
let simdIndex: Int? = withMemoryRebound(to: SIMD16<UInt8>.self, capacity: simdSize) { buffer in | |
let zero = SIMD16<UInt8>(repeating: 0) | |
return (0..<simdSize).first { buffer[$0] != zero } | |
} | |
// At least one byte is non-zero, find it within the chunk | |
if let simdIndex { | |
let index = simdIndex * stride | |
return (index..<index + stride).first { self[$0] != 0 }! | |
} | |
// Check any remaining bytes | |
let simdEnd = simdSize * stride | |
return (simdEnd..<bufferSize).first { self[$0] != 0 } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This function is called like this:
And returns the same as
The implementation used SIMD instructions to speed up an otherwise really slow operation.
Note: If
buffer
was anArray
not anUnsafeMutablePointer
, a more natural way to write this would bebuffer.firstIndex { $0 != 0 }
. But this has them same runtime penalty of iterating every item separately...