I have a large binary data file (big endian) with 100+ million "rows" of 11 elements, combination of floats and integers.
This is the format:
{"Real32", "Real32", "Real32", "Real32", "Real32", "Real32", "Real32", "Real32", "Real32", "Integer32", "Integer32"}
This question: How to read data file quickly?, is related but not exactly the same.
I've been reading in the whole file like this:
str = OpenRead[filename, BinaryFormat -> True];
data = BinaryReadList[str, {"Real32", "Real32", "Real32", "Real32", "Real32", "Real32", "Real32", "Real32", "Real32", "Integer32", "Integer32"}, ByteOrdering -> +1];
This requires lots and lots of memory and in the end I throw away most of the data most of the time. Usually I am just interested in the 4th real32 and the 2ndint32, or each "row". I would like to read the only the 4th real32 and the 2nd int32 of each "row" if possible and skip over the rest.
I've tried to use Skip
but the documentation isn't clear if it works with BinaryReadList
. I get the error Skip::readf: Real32 is not a valid format specification. "
.
The documentation doesn't describe that you can skip byte by byte, but you can...
str = OpenRead[name, BinaryFormat -> True];
count = FileByteCount[name]/(11*4);
reading = Table[{Skip[str, Byte, 12];
BinaryRead[str, "Real32", ByteOrdering -> +1],
Skip[str, Byte, 24];
BinaryRead[str, "Integer32", ByteOrdering -> +1]},
{count}]; // AbsoluteTiming
edit: This code works now, but it is very slow, about a minute to load a file that takes only 15 seconds with BinaryReadList
, however, the memory overhead is orders of magnitude lower.
edit2: Skip
appears to be very slow, much slower than SetStreamPosition
for some reason. So I wrote some new code that uses SetStreamPosition
with a precomputed list of StreamPositions in bytes. It is about twice as fast as the Skip
version, which is okay, but its still about 3x slower than BinaryReadList
pos = Range[12, FileByteCount[name], 11*4];
data = {SetStreamPosition[str, #];
BinaryRead[str, "Real32", ByteOrdering -> +1],
SetStreamPosition[str, # + 28];
BinaryRead[str, "Integer32", ByteOrdering -> +1]} & /@ pos; // AbsoluteTiming
Hopefully, someone will have an idea how this can be improved. Memory usage is still low, as expected.
I'm willing to tolerate a slight slow down (maybe 2x but not 5-10x) if there is a considerable memory savings to be gained but it would be great if the process could be sped up as well.
I can't really easily provide a copy of my data file as they are 100s of megabytes. I tried to write some code that generates some random data and writes it to a file, however, BinaryWrite
appears to be extremely slow... I'm on a fast machine with a solid state drive and its going only a few 100 kilobytes per second... Here is the code, regardless, maybe someone knows a faster way to make a random binary data file. This will make an ~40 MB file.
outputstr = OpenWrite["randomdata", BinaryFormat -> True]
reals = RandomReal[100, {10^6, 9}];
ints = RandomInteger[100, {10^6, 2}];
both = Flatten@Transpose@Join[Transpose@reals, Transpose@ints];
BinaryWrite[outputstr, both, {"Real32", "Real32", "Real32", "Real32",
"Real32", "Real32", "Real32", "Real32", "Real32", "Integer32",
"Integer32"}, ByteOrdering -> +1]
Close[outputstr]