I am using write( fd, buffer, count ) in io.h to write buffer into a file descriptor.
Now I found huge performance issue, because every time I call “write” it will perform a IO operation.
I want to add a buffer before actually writing content into file.
What is the best way to do this?
I searched and found this:
http://www.java2s.com/Tutorial/Cpp/0240__File-Stream/filedescriptoroutbuffer.htm
Should I use this method and integrate into my code? Is that a good approach I should take?
But in it, I don’t see how do define the buffer size.
Following is the final code:
class fdoutbuf : public std::streambuf {
protected:
enum { size = 4096 };
int fd; // file descriptor
char buf_[size];
public:
// constructor
fdoutbuf (int _fd) : fd(_fd) {
setp(this->buf_, this->buf_ + size - 1);
}
protected:
// write one character
virtual int overflow (int c){
if (c != EOF) {
char z = c;
*this->pptr() = z;
this->pbump(1);
}
return this->sync() == -1? EOF: c;
}
virtual int sync(){
if (this->pbase() == this->pptr()) {
return 0;
}
int count(this->pptr() - this->pbase());
int rc = write(fd, this->buf_, count);
this->setp(this->buf_, this->buf_ + size - 1);
return count == rc? 0: -1;
}
};
class fdostream : public std::ostream {
protected:
fdoutbuf buf;
public:
fdostream (int fd) : std::ostream(0), buf(fd) {
rdbuf(&buf);
}
};
Creating the buffering infrastructure isn’t really rocket science but also isn’t entirely trivial. Personally, I would just create a class derived from
std::streambufand either use the stream buffer abstraction or a stream (probably I would use the latter and accept the small performance impact introduced by most implementations). The work need to do this is fairly simple (this is for writing only):It seems it may be worth explaining what the stream buffer above actually does (given the lengthy discussion below). So, here is a breakdown:
std::vector<char>: the size would become a default parameter.setp()sets up a buffer used by the stream buffer, consisting of three pointers:pbase()is the start of the buffer, i.e. the first parameter tosetp()epptr()is the end of the buffer, i.e. the second parameter tosetp()pptr()is set to the same value aspbase()but is the pointer which is actually moved to indicate the position of the next character. If this isepptr()when a character is writtenoverflow()is called.overflow()is possibly given the character which caused the buffer to overflow. Unless it is called by user code with an argument ofeof()this is the case when the standard library calls the function. To deal with this extra character, the buffer is given one element less there is available space: the character can be appended to the buffer before the buffer is actually written.traits_typeinherited fromstd::streambufdefines a number of functions to determine properties of characters. When checking the parameter passed tooverflow()the functioneq_int_type()is used which compares to objects of the stream’s integer type for equality. It compares the argument tooverflow()with the value indicating an invalid character which is obtained usingeof(). All members of the traits type arestaticortypedefs, i.e. there is no need to use an object of this type. The use of thetraits_typeis important when using different template instantiations ofstd::basic_streambuf.pbase()toepptr()withpptr()between these two ends,pptr()can be moved beyondepptr()usingpbump(n): this function just addsntopptr(). This is used to put the currecnt character into the buffer right before callingsync()which just writes the content of the buffer and resets it.overflow()iseof()if the function failed. On successful execution the function returns something different thaneof()and conventionally the character passed as argument is used. As the argument may beeof()it isn’t good enough to just return the argument, though: if it iseof()this value needs to be transformed into some other useful value which is whatnot_eof()does: it returns it arguments unless the argument iseof()in which case it returns some suitable other value.sync()is responsible for flushing the buffer. In this case it is first checking that there is something to be done and, if so, writes the content of the buffer. There isn’t really any trick in this function although I guess a proper one would be more careful if thewrite()fails, i.e. returns fewer characters than are characters in the buffer. This version just pretends that it is OK to loose characters which couldn’t be written although it will indicate an error.-1, on success it return0. No trick.