L
Lionel B
Greetings,
I have some code that is to read unformatted data from disc and interpret
it as blocks of unsigned integers. In an attempt to achieve efficiency
(it is pretty essential for my application that the code be
speed optimized [*]) I use reinterpret_cast to alias a block of chars read
in from disc as a block of integer "words". My existing code (see
simplified code below) appears to work well enough on the platforms
available to me, but I would like to achieve maximum portability and would
really appreciate any commentary on possible portability (and also memory
alignment) issues that might arise from my approach.
I understand that in some sense all bets are off when using
reinterpret_cast as details will be implementation-defined but I can't see
why my aliasing method (with suitable checks) should cause any problems.
As to memory alignment for efficient access to my memory buffer, I'm not
sure how global operator new handles memory alignment (I shouldn't think
the standard has anything to say about this), so I might be prepared to
either (i) "manually" align my memory blocks - not actually sure how to do
this or how much portability might be achieved with this approach) - or
(ii) use malloc / memalign, whatever, from the (or rather "a") C library,
where alignment behaviour might be more precisely specified (eg the GNU C
library). Of course there will be portability issues with this approach.
In essence my problem seems to involve a potential trade-off between
efficiency and portability. Again, any comments, suggestions, alternative
approaches welcome.
Simplified code below.
[*] The code to be used in anger is to grab random numbers from huge
binary data files for a statistical application ... I have identified
random number manipulation as a time bottleneck.
--- BEGIN CODE ----
// main.cpp
#include <iostream>
#include <fstream>
#include <string>
// Read unformatted data from disc and interpret as unsigned integers
int main()
{
// the word type (an unsigned integer type - season to taste)
typedef unsigned long word_t;
// number of chars per word
const size_t wordsize = sizeof(word_t);
std::cout << "wordsize = " << wordsize << '\n';
// data file (we open with ios::ate since we want to get file size first)
const std::string bitsfile = "bits.dat";
std::ifstream bfs(bitsfile.c_str(),std::ios::in|std::ios::binary|std::ios::ate);
if (!bfs.is_open()) {
std::cerr << "failed to open file " << bitsfile << '\n';
return 1;
}
// get file size
const size_t cfilesize = bfs.tellg();
std::cout << "filesize = " << cfilesize << " chars\n";
// number of bytes (chars) to read - mustn't be bigger than file size!
const size_t cblocksize = 128;
std::cout << "block size (char) = " << cblocksize << '\n';
if (cblocksize>cfilesize) {
std::cerr << "block size cannnot be larger than file size\n";
return 1;
}
// number of words to write - must be divisible by word size!
const size_t wblocksize = cblocksize/wordsize;
std::cout << "block size (word) = " << wblocksize << '\n';
if (wblocksize*wordsize != cblocksize) {
std::cerr << "block size must be divisible by word size\n";
return 1;
}
// char buffer for reading
// COMMENTS? alignement issues?
char* const cbuf(new char[cblocksize]);
// word buffer for writing aliased to char buffer
// COMMENTS? how portable is this...?
// COMMENTS? alignement issues?
const word_t* const wbuf(reinterpret_cast<word_t* const>(cbuf));
// seek to beginning of file and read block of chars
bfs.seekg(0);
bfs.read(cbuf,cblocksize);
bfs.close();
// write out words
for (size_t i;i<wblocksize;++i) std::cout << "word " << i << " = " << wbuf << '\n';
// clean up
delete [] cbuf;
return 0;
}
--- END CODE ----
I have some code that is to read unformatted data from disc and interpret
it as blocks of unsigned integers. In an attempt to achieve efficiency
(it is pretty essential for my application that the code be
speed optimized [*]) I use reinterpret_cast to alias a block of chars read
in from disc as a block of integer "words". My existing code (see
simplified code below) appears to work well enough on the platforms
available to me, but I would like to achieve maximum portability and would
really appreciate any commentary on possible portability (and also memory
alignment) issues that might arise from my approach.
I understand that in some sense all bets are off when using
reinterpret_cast as details will be implementation-defined but I can't see
why my aliasing method (with suitable checks) should cause any problems.
As to memory alignment for efficient access to my memory buffer, I'm not
sure how global operator new handles memory alignment (I shouldn't think
the standard has anything to say about this), so I might be prepared to
either (i) "manually" align my memory blocks - not actually sure how to do
this or how much portability might be achieved with this approach) - or
(ii) use malloc / memalign, whatever, from the (or rather "a") C library,
where alignment behaviour might be more precisely specified (eg the GNU C
library). Of course there will be portability issues with this approach.
In essence my problem seems to involve a potential trade-off between
efficiency and portability. Again, any comments, suggestions, alternative
approaches welcome.
Simplified code below.
[*] The code to be used in anger is to grab random numbers from huge
binary data files for a statistical application ... I have identified
random number manipulation as a time bottleneck.
--- BEGIN CODE ----
// main.cpp
#include <iostream>
#include <fstream>
#include <string>
// Read unformatted data from disc and interpret as unsigned integers
int main()
{
// the word type (an unsigned integer type - season to taste)
typedef unsigned long word_t;
// number of chars per word
const size_t wordsize = sizeof(word_t);
std::cout << "wordsize = " << wordsize << '\n';
// data file (we open with ios::ate since we want to get file size first)
const std::string bitsfile = "bits.dat";
std::ifstream bfs(bitsfile.c_str(),std::ios::in|std::ios::binary|std::ios::ate);
if (!bfs.is_open()) {
std::cerr << "failed to open file " << bitsfile << '\n';
return 1;
}
// get file size
const size_t cfilesize = bfs.tellg();
std::cout << "filesize = " << cfilesize << " chars\n";
// number of bytes (chars) to read - mustn't be bigger than file size!
const size_t cblocksize = 128;
std::cout << "block size (char) = " << cblocksize << '\n';
if (cblocksize>cfilesize) {
std::cerr << "block size cannnot be larger than file size\n";
return 1;
}
// number of words to write - must be divisible by word size!
const size_t wblocksize = cblocksize/wordsize;
std::cout << "block size (word) = " << wblocksize << '\n';
if (wblocksize*wordsize != cblocksize) {
std::cerr << "block size must be divisible by word size\n";
return 1;
}
// char buffer for reading
// COMMENTS? alignement issues?
char* const cbuf(new char[cblocksize]);
// word buffer for writing aliased to char buffer
// COMMENTS? how portable is this...?
// COMMENTS? alignement issues?
const word_t* const wbuf(reinterpret_cast<word_t* const>(cbuf));
// seek to beginning of file and read block of chars
bfs.seekg(0);
bfs.read(cbuf,cblocksize);
bfs.close();
// write out words
for (size_t i;i<wblocksize;++i) std::cout << "word " << i << " = " << wbuf << '\n';
// clean up
delete [] cbuf;
return 0;
}
--- END CODE ----