@Krux02, yes, the data that I will be reading will vary in endianness, which
adds an extra complication for this. In the case where the file's endianness
does not match the machine's, I intended to perform byte swapping. @Araq, I'm a
little surprised by this. I had done a quick performance comparison of the
three proposed approaches and found this:
import times, endians, strutils
when isMainModule:
let
numIter = 100_000_000
offset = 1
startTime1 = epochTime()
var
z: seq[byte] = @[0'u8, 1, 0, 0, 0, 0, 0, 0, 0]
sum = 0'u64
# Approach 1, casting followed by bit shifting
for i in 0 .. < numIter:
sum += (cast[uint64](z[offset]) shl 0) or
(cast[uint64](z[offset+1]) shl 8) or
(cast[uint64](z[offset+2]) shl 16) or
(cast[uint64](z[offset+3]) shl 24) or
(cast[uint64](z[offset+4]) shl 32) or
(cast[uint64](z[offset+5]) shl 40) or
(cast[uint64](z[offset+6]) shl 48) or
(cast[uint64](z[offset+7]) shl 56)
let endTime1 = epochTime()
echo "Solution 1 required ", endTime1 - startTime1, " seconds to count to
", sum, "."
# Approach 2, direct casting
sum = 0
let startTime2 = epochTime()
let p = 1
for i in 0 .. < numIter:
sum += (cast[ptr uint64](addr z[p]))[]
let endTime2 = epochTime()
echo "Solution 2 required ", endTime2 - startTime2, " seconds to count to
", sum, "."
# Approach 3, use of endians
let startTime3 = epochTime()
var v = 0'u64
sum = 0
for i in 0 .. < numIter:
littleEndian64(addr v, addr z[offset])
sum += v
let endTime3 = epochTime()
echo "Solution 3 required ", endTime3 - startTime3, " seconds to count to
", sum, "."
And on my machine the results are:
> Solution 1 required 4.861401081085205 seconds to count to 100000000.
>
> Solution 2 required 0.9038379192352295 seconds to count to 100000000.
>
> Solution 3 required 2.14686393737793 seconds to count to 100000000.
The first solution was by far the slowest of the approaches with the direct
casting approach clearly the performance winner. I realize that you would know
far better than I would in this situation, so is there perhaps something that
I'm doing wrong above?