How can I make my char buffer more performant? - c++

I have to read a lot of data into:
vector<char>
A 3rd party library reads this data in many turns. In each turn it calls my callback function whose signature is like this:
CallbackFun ( int CBMsgFileItemID,
unsigned long CBtag,
void* CBuserInfo,
int CBdataSize,
void* CBdataBuffer,
int CBisFirst,
int CBisLast )
{
...
}
Currently I have implemented a buffer container using an STL Container where my method insert() and getBuff are provided to insert a new buffer and getting stored buffer. But still I want better performing code, so that I can minimize allocations and de-allocations:
template<typename T1>
class buffContainer
{
private:
class atomBuff
{
private:
atomBuff(const atomBuff& arObj);
atomBuff operator=(const atomBuff& arObj);
public:
int len;
char *buffPtr;
atomBuff():len(0),buffPtr(NULL)
{}
~atomBuff()
{
if(buffPtr!=NULL)
delete []buffPtr;
}
};
public :
buffContainer():_totalLen(0){}
void insert(const char const *aptr,const unsigned long &alen);
unsigned long getBuff(T1 &arOutObj);
private:
std::vector<atomBuff*> moleculeBuff;
int _totalLen;
};
template<typename T1>
void buffContainer< T1>::insert(const char const *aPtr,const unsigned long &aLen)
{
if(aPtr==NULL,aLen<=0)
return;
atomBuff *obj=new atomBuff();
obj->len=aLen;
obj->buffPtr=new char[aLen];
memcpy(obj->buffPtr,aPtr,aLen);
_totalLen+=aLen;
moleculeBuff.push_back(obj);
}
template<typename T1>
unsigned long buffContainer<T1>::getBuff(T1 &arOutObj)
{
std::cout<<"Total Lenght of Data is: "<<_totalLen<<std::endl;
if(_totalLen==0)
return _totalLen;
// Note : Logic pending for case size(T1) > T2::Value_Type
int noOfObjRqd=_totalLen/sizeof(T1::value_type);
arOutObj.resize(noOfObjRqd);
char *ptr=(char*)(&arOutObj[0]);
for(std::vector<atomBuff*>::const_iterator itr=moleculeBuff.begin();itr!=moleculeBuff.end();itr++)
{
memcpy(ptr,(*itr)->buffPtr,(*itr)->len);
ptr+= (*itr)->len;
}
std::cout<<arOutObj.size()<<std::endl;
return _totalLen;
}
How can I make this more performant?

If my wild guess about your callback function makes sense, you don't need anything more than a vector:
std::vector<char> foo;
foo.reserve(MAGIC); // this is the important part. Reserve the right amount here.
// and you don't have any reallocs.
setup_callback_fun(CallbackFun, &foo);
CallbackFun ( int CBMsgFileItemID,
unsigned long CBtag,
void* CBuserInfo,
int CBdataSize,
void* CBdataBuffer,
int CBisFirst,
int CBisLast )
{
std::vector<char>* pFoo = static_cast<std::vector<char>*>(CBuserInfo);
char* data = static_cast<char*>CBdataBuffer;
pFoo->insert(pFoo->end(), data, data+CBdataSize);
}

Depending on how you plan to use the result, you might try putting the incoming data into a rope datastructure instead of vector, especially if the strings you expect to come in are very large. Appending to the rope is very fast, but subsequent char-by-char traversal is slower by a constant factor. The tradeoff might work out for you or not, I don't know what you need to do with the result.
EDIT: I see from your comment this is no option, then. I don't think you can do much more efficient in the general case when the size of the data coming in is totally arbitrary. Otherwise you could try to initially reserve enough space in the vector so that the data will fit without or at most one reallocation in the average case or so.
One thing I noticed about your code:
if(aPtr==NULL,aLen<=0)
I think you mean
if(aPtr==NULL || aLen<=0)

The main thing you can do is avoid doing quite so much copying of the data. Right now, when insert() is called, you're copying the data into your buffer. Then, when getbuff() is called, you're copying the data out to a buffer they've (hopefully) specified. So, to get data from outside to them, you're copying each byte twice.
This part:
arOutObj.resize(noOfObjRqd);
char *ptr=(char*)(&arOutObj[0]);
Seems to assume that arOutObj is really a vector. If so, it would be a whole lot better to rewrite getbuff as a normal function taking a (reference to a) vector instead of being a template that really only works for one type of parameter.
From there, it becomes a fairly simple matter to completely eliminate one copy of the data. In insert(), instead of manually allocating memory and tracking the size, put the data directly into a vector. Then, when getbuff() is called, instead of copying the data into their buffer, just give then a reference to your existing vector.
class buffContainer {
std::vector<char> moleculeBuff;
public:
void insert(char const *p, unsigned long len) {
Edit: Here you really want to add:
moleculeBuff.reserve(moleculeBuff.size()+len);
End of edit.
std::copy(p, p+len, std::back_inserter(moleculeBuff));
}
void getbuff(vector<char> &output) {
output = moleculeBuff;
}
};
Note that I've changed the result of getbuff to void -- since you're giving them a vector, its size is known, and there's no point in returning the size. In reality, you might want to actually change the signature a bit, to just return the buffer:
vector<char> getbuff() {
vector<char> temp;
temp.swap(moleculeBuff);
return temp;
}
Since it's returning a (potentially large) vector by value, this depends heavily on your compiler implementing the named return value optimization (NRVO), but 1) the worst case is that it does about what you were doing before anyway, and 2) virtually all reasonably current compilers DO implement NRVO.
This also addresses one other detail your original code didn't (seem to). As it was, getbuff returns some data, but if you call it again, it (apparently doesn't keep track of what data has already been returned, so it returns it all again. It keeps allocating data, but never deletes any of it. That's what the swap is for: it creates an empty vector, and then swaps that with the one that's being maintained by buffContainer, so buffContainer now has an empty vector, and the filled one is handed over to whatever called getbuff().
Another way to do things would be to take the swap a step further: basically, you have two buffers:
one owned by buffContainer
one owned by whatever calls getbuffer()
In the normal course of things, we can probably expect that the buffer sizes will quickly reach some maximum size. From there on, we'd really like to simply re-cycle that space: read some data into one, pass it to be processed, and while that's happening, read data into the other.
As it happens, that's pretty easy to do too. Change getbuff() to look something like this:
void getbuff(vector<char> &output) {
swap(moleculeBuff, output);
moleculeBuff.clear();
}
This should improve speed quite a bit -- instead of copying data back and forth, it just swaps one vector's pointer to the data with the others (along with a couple other details like the current allocation size, and used size of the vector). The clear is normally really fast -- for a vector (or any type without a dtor) it'll just set the number of items in the vector to zero (if the items have dtors, it has to destroy them, of course). From there, the next time insert() is called, the new data will just be copied into the memory the vector already owns (until/unless it needs more space than the vector had allocated).

Related

Why is the dynamically allocated array attribute of my class template only able to store one item?

I am trying to expand the functionality of a class template I created. Previously it allowed you to use key-value pairs of any type but only if you knew the size of the arrays at compile time. It looked like this:
template <typename K, typename V, int N>
class KVList {
size_t arraySize;
size_t numberOfElements;
K keys[N];
V values[N];
public:
KVList() : arraySize(N), numberOfElements(0) { }
// More member functions
}
I wanted to be able to use this for a dynamic number of elements decided at run-time, so I changed the code to this:
template <typename K, typename V>
class KVList {
size_t arraySize;
size_t numberOfElements;
K* keys;
V* values;
public:
KVList(size_t size) : numberOfElements(0) {
arraySize = size;
keys = new K[size];
values = new V[size];
}
~KVList() {
delete[] keys;
keys = nullptr;
delete[] values;
values = nullptr;
}
// More member functions
}
The new constructor has one parameter which is the size that will be used for the KVList. It still starts the numberOfElements at 0 because both of these uses would start the KVList empty, but it does set arraySize to the value of the size parameter. Then it dynamically allocated memory for the arrays of keys and values. An added destructor deallocates the memory for these arrays and then sets them to nullptr.
This compiles and runs, but it only stores the first key and first value I try to add to it. There is a member function in both that adds a key-value pair to the arrays. I tested this with the Visual Studio 2015 debugger and noticed it storing the first key-value pair fine, and then it attempts to store the next key-value pair in the next index, but the data goes no where. And the debugger only shows one slot in each array. When I attempt to cout the data I thought I stored at that second index, I get a very small number (float data type was trying to be stored), not the data I was trying to store.
I understand it might be worth using the vectors to accomplish this. However, this is an expansion on an assignment I completed in my C++ class in school and my goal with doing this was to try to get it done, and understand what might cause issues doing it this way, since this is the obvious way to me with the knowledge I have so far.
EDIT: Code used to add a key-value pair:
// Adds a new element to the list if room exists and returns a reference to the current object, does nothing if no room exists
KVList& add(const K& key, const V& value) {
if (numberOfElements < arraySize) {
keys[numberOfElements] = key;
values[numberOfElements] = value;
numberOfElements++;
}
return *this;
}
EDIT: Code that calls add():
// Temp strings for parts of a grade record
string studentNumber, grade;
// Get each part of the grade record
getline(fin, studentNumber, subGradeDelim); // subGradeDelim is a char whose value is ' '
getline(fin, grade, gradeDelim); // gradeDelim is a char whose value is '\n'
// Attempt to parse and store the data from the temp strings
try {
data.add(stoi(studentNumber), stof(grade)); // data is a KVList<size_t, float> attribute
}
catch (...) {
// Temporary safeguard, will implement throwing later
data.add(0u, -1);
}
Code used to test displaying the info:
void Grades::displayGrades(ostream& os) const {
// Just doing first two as test
os << data.value(0) << std::endl;
os << data.value(1);
}
Code in main cpp file used for testing:
Grades grades("w6.dat");
grades.displayGrades(cout);
Contents of w6.dat:
1022342 67.4
1024567 73.5
2031456 79.3
6032144 53.5
1053250 92.1
3026721 86.5
7420134 62.3
9762314 58.7
6521045 34.6
Output:
67.4
-1.9984e+18
The problem (or at least one of them) is with this line from your pastebin:
data = KVList<size_t, float>(records);
This seemingly innocent line is doing a lot. Because data already exists, being default constructed the instance that you entered the body of the Grades constructor, this will do three things:
It will construct a KVList on the right hand side, using its constructor.
It will call the copy assignment operator and assign what we constructed in step 1 to data.
The object on the right hand side gets destructed.
You may be thinking: what copy assignment operator, I never wrote one. Well, the compiler generates it for you automatically. Actually, in C++11, generating a copy assignment operator automatically with an explicit destructor (as you have) is deprecated; but it's still there.
The problem is that the compiler generated copy assignment operator does not work well for you. All your member variables are trivial types: integers and pointers. So they just copied over. This means that after step 2, the class has just been copied over in the most obvious way. That, in turn, means that for a brief instance, there is an object on the left and right, that both have pointers pointing to the same place in memory. When step 3 fires, the right hand object actually goes ahead and deletes the memory. So data is left with pointers pointing to random junk memory. Writing to this random memory is undefined behavior, so your program may do (not necessarily deterministic) strange things.
There are (to be honest) many issues with how your explicit resource managing class is written, too many to be covered here. I think that in Accelerated C+, a really excellent book, it will walk you through these issues, and there is an entire chapter covering every single detail of how to properly write such a class.

Adding items outside of allocated array?

I have a class Set:
class Set
{
public:
//Default constructor
Set ();
//Some more functions...
private:
int *p;
const int K = 10;
int numval = 0; //Number of ints in the array
//Other variables...
};
The default constructor:
Set::Set()
{
p = new int[K]; //Allocate memory for array with 10 ints
}
If I in some other function would fill the array with 10 ints and then add an other one, what would happen? The compiler doesn't crash and I'm able to print the 11:th int. But since I havn't allocated memory for it, where is it stored?
Example:
Set1 += 5;
Would add 5 to the array with the following operator overloader.
const Set& Set::operator+=(const int x)
{
p[numval] = x; //Add next int after the last int in the array
numval++; //Increment number of ints
return *this;
}
If I in some other function would fill the array with 10 ints and then add an other one, what would happen?
You'd write into whatever memory came after the end of the array, causing undefined behaviour: perhaps causing no obvious problems, perhaps corrupting some unrelated data (or the metadata used to manage the heap), or perhaps crashing if there was no writable memory there.
But since I havn't allocated memory for it, where is it stored?
It isn't stored anywhere, in the sense of having storage allocated for it. There's just nothing to stop you writing to arbitrary memory locations beyond the end of an array. Be careful not to do that.
Computer memory is linear. It's one huge row of cells (bytes). Every cell has 2 neighbours (except the first and the last ones, obviously). Allocating memory is just an act of telling "this part is mine". It's really nothing more than a promise: you promise to not write outside your plot and in return you get promise noone else would write inside it. So what happens when you write outside of your allocated area? You break your promise. There may be someone's else's plot right next to yours, there might be unused space. Nothing really happens when you write outside your area. Real problem arises when rightful owner comes back and tries to pick up what he left - and it turns out to be something else, something you put there. (Of course it's possible that your plot lies next to something system considers important. In that case, OS stations guards on the border, and they shot to kill any trespassers on sight.)
It is your job as a programmer to make your program keep it's promises. When processes break their promises, bad things may or may not happen - to them or to other processes.

Container with non linear contents

I apologize if this question has been asked/answered elsewhere, but I didn't find anything since I'm not entirely sure what/how to ask this...
What I'm attempting to do is set up some kind of container; list, array, vector, what-ever, that will allow me to place and remove objects from specific indices.
Something like this:
[empty][empty][object][empty][object][object][empty]
I'm going to be moving objects from a vector into a specific index of this container and from this container to another vector.
What would be the best way to represent this and what kind of container would be best suited? I was originally using a vector, but the built in functions didn't seem to give me enough control over where the object ended up. It was either the front or the back.
I need to figure out the best way to hold those "empty" indices and move objects in and out of each element freely.
A simple, definitely sub-optimal, but quite effective solution could be to use a vector in the following way:
#include <iostream>
#include <vector>
using namespace std;
struct Your_Object
{
Your_Object& operator=(const Your_Object& other)
{
// Write a proper assignment operator here if you want to assign or swap values
cout << "hello from assignment operator"<<endl;
return *this;
}
};
int main() {
Your_Object nullObj;
std::vector<Your_Object> vec;
vec.reserve(10); // Creates 10 empty objects calling default constructors. Notice that this will NOT affect the vector's size, for that use resize()
Your_Object space5, space3; // Two objects to put in space5 and space3
// Put objects in space 5 and 3
vec[5] = space5;
vec[3] = space3;
// Move object in space 5 to another place
vec[1] = vec[5];
return 0;
}
http://ideone.com/YDu6LC
If you manage to write a proper copy-assignment operator (perhaps with move semantics if you're using C++11) which does a deep-copy of your object and if of course this proves to be not too burdensome for you, the above might be a simple working system for what you need to do.
Just remember to resize (or reserve, for the difference take a look here: https://stackoverflow.com/a/7397862/1938163) the space you need in advance.
If I understand your question correctly you want to place data in your vector according to a certain pattern.
You can use a simple vector and implement functions yourself to place your data.
For example if you want to place data in every third place :
void placeItem(std::vector<int> my_vector, int element, unsigned int index){
my_vector[((index+1)*3)-1]=element;
}
int retreiveItem(std::vector<int> my_vector, unsigned int index){
return my_vector[((index+1)*3)-1];
}
Then you can use placeItem and retreiveItem with indexes starting at 0.
If you simply meant that you want to place your data in arbitrary locations then you can use the [] syntax directly.

Which container to use for String-Interning

My goal is to do string-interning. For this I am looking for a hashed
container class that can do the following:
allocate only one block of memory per node
different userdata size per node
The value type looks like this:
struct String
{
size_t refcnt;
size_t len;
char data[];
};
Every String object will have a different size. This will be accomplished with
opereator new + placement new.
So basically I want to allocate the Node myself and push it in the container later.
Following containers are not suitable:
std::unordored_set
boost::multi_index::*
Cannot allocate different sized nodes
boost::intrusive::unordered_set
Seems to work at first. But has some drawbacks. First of all you have to allocate
the bucket array and maintain the load-factor yourself. This is just unnecessary
and error-prone.
But another problem is harder to solve: You can only search for objects that have the
type String. But it is inefficient to allocate a String everytime you look for an entry
and you only have i.e. a std::string as input.
Are there any other hashed containers that can be used for this task?
I don't think you can do that with any of the standard containers.
What you can do is store the pointer to String and provide custom hash and cmp functors
struct StringHash
{
size_t operator() (String* str)
{
// calc hash
}
};
struct StringCmp
{
bool operator() (String* str1, String* str2)
{
// compare
}
};
std::unordered_set<String*, StringHash, StringCmp> my_set;
Your definition for String won't compile in C++; the obvious
solution is to replace the data field with a pointer (in which
case, you can put the structures themselves in
std::unordered_set).
It's possible to create an open ended struct in C++ with
something like the following:
struct String
{
int refcnt;
int len;
char* data()
{
return reinterpret_cast<char*>(this + 1);
}
};
You're skating on thin ice if you do, however; for types other
than char, there is a risk that this + won't be
appropriately aligned.
If you do this, then your std::unordered_set will have to
contain pointers, rather than the elements, so I doubt you'll
gain anything for the effort.

Dynamically creating std::vector // creating a pointer to a vector

I am new so I more than likely missing something key.
I am using std::vector to store data from a ReadFile operation.
Currently I have a structure called READBUFF that contains a vector of byte. READBUFF is then instantiated via a private type in a class called Reader.
class Reader{
public:
void Read();
typedef struct {
std::vector<byte> buffer;
} READBUFF;
private:
READBUFF readBuffer;
}
Within Read() I currently resize the array to my desired size as the default allocator creates a really large vector [4292060576]
void Reader::Read()
{
readBuffer.buffer.resize(8192);
}
This all works fine, but then I got to thinking I'd rather dynamically NEW the vector inline so I control the allocation management of the pointer. I changed buffer to be: std::vector* buffer. When I try to do the following buffer is not set to a new buffer. It's clear from the debugger that it is not initialized.
void Reader::Read()
{
key.buffer = new std::vector<byte>(bufferSize);
}
So then I tried, but this behaves the same as above.
void Reader::Read()
{
std::vector<byte> *pvector = new std::vector<byte>(8192);
key.buffer = pvector;
}
Main first question is why doesn't this work? Why can't I assign the buffer pointer to valid pointer? Also how do I define the size of the inline allocation vs. having to resize?
My ultimate goal is to "new up" buffers and then store them in a deque. Right now I am doing this to reuse the above buffer, but I am in essence copying the buffer into another new buffer when all I want is to store a pointer to the original buffer that was created.
std::vector<byte> newBuffer(pKey->buffer);
pKey->ptrFileReader->getBuffer()->Enqueue(newBuffer);
Thanks in advance. I realize as I post this that I missing something fundamental but I am at a loss.
You shouldn't be using new in this case. It causes you to have to manage the memory manually, which is never something you should want to do for many reasons1. You said you want to manage the lifetime of the vector by using new; in reality, the lifetime of the vector is already managed because it's the same as the object that holds it. So the lifetime of that vector is the lifetime of the instance of your Reader class.
To set the size of the vector before it gets constructed, you'll have to make a constructor for READBUFF:
// inside the READBUFF struct (assuming you're using a normal variable, not a pointer)
READBUFF() { } // default constructor
READBUFF(int size) : buffer(size) { } // immediately sets buffer's size to the argument
and use an initialization list in Reader's constructor:
// inside the Reader class
Reader() : readBuffer(8092) { }
Which will set the readBuffer.buffer's size to 8092.
If you really want to use new just for learning:
key.buffer = new std::vector<byte>(bufferSize);
This will work fine, but you shouldn't be doing it in the Read function, you should be doing it in the object's constructor. That way any member function can use it without having to check if it's NULL.
as the default allocator creates a really large vector [4292060576]
No, it doesn't (if it did, you could have one vector on your entire computer and probably your computer would crash). It incrementally resizes the storage up when you add things and exceed the capacity. Using resize like you are doing is still good though, because instead of allocating a small one, filling it, allocating a bigger one and copying everything over, filling it, allocating a bigger one and copying everything over, etc. you are just allocating the size you need once, which is much faster.
1 Some reasons are:
You have to make sure to allocate it before anyone else uses it, where with a normal member variable it's done automatically before your object has a chance to use it.
You have to remember to delete it in the destructor.
If you don't do the above 2 things, you have either a segfault or a memory leak.
I think you may be misinterpreting the result of calling max_size() on a vector:
#include <vector>
#include <iostream>
int main() {
std::cout << std::vector<char>().max_size() << std::endl;
std::cout << std::vector<char>::size_type(~0) << std::endl;
}
This program prints the maximum possible size of the vector, not the current size. size() on the other hand does print the current size (ignoring anything that's been reserved).