This really is a question just for my own interest I haven't been able to determine through the documentation.
I see on http://www.cplusplus.com/reference/string/string/ that append has complexity:
"Unspecified, but generally up to linear in the new string length."
while push_back() has complexity:
"Unspecified; Generally amortized constant, but up to linear in the new string length."
As a toy example, suppose I wanted to append the characters "foo" to a string. Would
myString.push_back('f');
myString.push_back('o');
myString.push_back('o');
and
myString.append("foo");
amount to exactly the same thing? Or is there any difference? You might figure that append would be more efficient because the compiler would know how much memory is required to extend the string the specified number of characters, while push_back may need to secure memory each call?
In C++03 (for which most of "cplusplus.com"'s documentation is written), the complexities were unspecified because library implementers were allowed to do Copy-On-Write or "rope-style" internal representations for strings. For instance, a COW implementation might require copying the entire string if a character is modified and there is sharing going on.
In C++11, COW and rope implementations are banned. You should expect constant amortized time per character added or linear amortized time in the number of characters added for appending to a string at the end. Implementers may still do relatively crazy things with strings (in comparison to, say std::vector), but most implementations are going to be limited to things like the "small string optimization".
In comparing push_back and append, push_back deprives the underlying implementation of potentially useful length information which it might use to preallocate space. On the other hand, append requires that an implementation walk over the input twice in order to find that length, so the performance gain or loss is going to depend on a number of unknowable factors such as the length of the string before you attempt the append. That said, the difference is probably extremely Extremely EXTREMELY small. Go with append for this -- it is far more readable.
I had the same doubt, so I made a small test to check this (g++ 4.8.5 with C++11 profile on Linux, Intel, 64 bit under VmWare Fusion).
And the result is interesting:
push :19
append :21
++++ :34
Could be possible this is because of the string length (big), but the operator + is very expensive compared with the push_back and the append.
Also it is interesting that when the operator only receives a character (not a string), it behaves very similar to the push_back.
For not to depend on pre-allocated variables, each cycle is defined in a different scope.
Note : the vCounter simply uses gettimeofday to compare the differences.
TimeCounter vCounter;
{
string vTest;
vCounter.start();
for (int vIdx=0;vIdx<1000000;vIdx++) {
vTest.push_back('a');
vTest.push_back('b');
vTest.push_back('c');
}
vCounter.stop();
cout << "push :" << vCounter.elapsed() << endl;
}
{
string vTest;
vCounter.start();
for (int vIdx=0;vIdx<1000000;vIdx++) {
vTest.append("abc");
}
vCounter.stop();
cout << "append :" << vCounter.elapsed() << endl;
}
{
string vTest;
vCounter.start();
for (int vIdx=0;vIdx<1000000;vIdx++) {
vTest += 'a';
vTest += 'b';
vTest += 'c';
}
vCounter.stop();
cout << "++++ :" << vCounter.elapsed() << endl;
}
Add one more opinion here.
I personally consider it better to use push_back() when adding characters one by one from another string. For instance:
string FilterAlpha(const string& s) {
string new_s;
for (auto& it: s) {
if (isalpha(it)) new_s.push_back(it);
}
return new_s;
}
If using append()here, I would replace push_back(it) with append(1,it), which is not that readable to me.
Yes, I would also expect append() to perform better for the reasons you gave, and in a situation where you need to append a string, using append() (or operator+=) is certainly preferable (not least also because the code is much more readable).
But what the Standard specifies is the complexity of the operation. And that is generally linear even for append(), because ultimately each character of the string being appended (and possible all characters, if reallocation occurs) needs to be copied (this is true even if memcpy or similar are used).
Related
The following piece of code:
string a = "abc";
cout << a.capacity();
a.erase(a.begin() + 1, a.end());
cout << a.capacity();
...outputs:
33
Even if I remove some elements from the string, the capacity remains the same. So my questions are:
Is some memory being held up because of capacity? What if I have not explicitly reserve()'d?
If I use reserve() and don't end up using the entire capacity, am I wasting memory?
Will this extra memory (which I am not using) be allocated to something else if required?
EDIT:
Suppose i have
string a= "something";
a = "ab";
Now I know that a won't ever have more than two characters. So is it wise to call reserve(2) so that memory is not wasted?
I'll answer your questions first:
The memory belongs to the string, but isn't used entirely. If you don't reserve, you can't control the capacity. You just know it is sufficiently large.
Correct.
As said in 1): no. It belongs to the string. Nothing else can use this memory. The string itself could use it for additional characters though.
For further details I'd recommend the documentation of string::reserve.
One final remark: If you don't ever reserve, everything will work fine - it might be unnecessarily slow though. That is only ever the case if you were to frequently add few characters and the string has to re-alloc alot (much like a vector). Reserving is basically intended to bypass this situation.
On the addendum: Calling reserve can help to save memory. If you call reserve(n), this ensures the string has an internal capacity for at least n characters. Note that reserve is not required to set the capacity to exactly n nor to reduce the capacity at all for small n (cf. reserve documentation).
Back to your example: If you call reserve it can never do any harm. It's the best you can do in general. (In case you have C++11 features, I'd recommend shrink_to_fit).
I tested with (older) versions of gcc / clang in which cases the capacity of a got changed to exactly 2. Since I'm not 100% sure what the added question referes to, here is what I ran:
auto a = string{"something"};
a = "ab";
cout << a << " " << a.capacity() << endl;
a.reserve(2);
cout << a << " " << a.capacity() << endl;
Which produces:
ab 9
ab 2
1) Is some memory being held up because of capacity? What if i have not
explicitly reserve()'d?
Even if you did not call reserve, the std::string object can still hold up some memory1 for (even a default constructed) std::string. And this is true with std::string implementations that uses Short String Optimization
2)If i use std::reserve() and dont end up using the entire capcity
then am i wasting memory?
It depends on what you mean by wasting memory; std::string dynamically resizes its buffer to accommodate changes in the size of the string. Well, in the case of Short String Optimized std::string implementations, there is nothing you can do about it. The memory is in the string object itself.
3)Will this extra memory which i am not using be allocated to
something else, if required?
No. A std::string object manages the memory it allocated, and it may or may not give it up2 wholly or partly, until its destroyed. See David Schwartz's comment
EDIT: Suppose i have
string a= "something";
Now i edit the string and know that a won't have more than two
characters.So is it wise to call a.reserve(2) so that memory is not
wasted?
If you modified a in a way that changes a.size(), such as calling resize method or assigning it to a new string of length 2, then the proceeding reserve call can2 be beneficial.
Note that, calling reserve would not reduce the string's contents. std::string::reserve is not permitted to truncate the string. It is only permitted to work on unused memory. If you call std::string::reserve(new_capacity_intended) with new_capacity_intended less than the size() of the string, the best that could possibly happen is the same effect of std::string::shrink_to_fit.
To reduce the string's memory (if the implementation does a binding shrink_to_fit request) and shrink it to the first two characters:
string a= "something";
//resize it first
a.resize(2); //or by some assignment such as a = "so";
//then
a.reserve(2); // or better still a.shrink_to_fit();
1: by memory, I assume Virtual Memory
2: std::string::reserve or std::string::shrink_to_fit may or may not give up the string's unused memory.
This question already has answers here:
Most optimized way of concatenation in strings
(9 answers)
Closed 9 years ago.
Consider first that the amount of total data that will be stored in the output string will almost certainly be small and so I doubt any of these have a noticeable affect on performance. My primary goal is to find a way to concatenate a series of const char*'s of unknown size that doesn't look terrible while also keeping efficiency in mind. Below are the results of my search:
Method 1:
std::string str = std::string(array1) + array2 + array3;
Method 2:
std::string str(array1);
str += array2;
str += array3;
I decided on the first method as it is short and concise. If I'm not mistaken, both methods will invoke the same series of operations. the unoptimized compiler would first create a temporary string and internally allocate some amount of space for its buffer >= sizeof(array1). If that buffer is sufficiently large, the additional + operations will not require any new allocations. Finally, if move semantics are supported, then the buffers of the temporary and named str are swapped.
Are there any other ways to perform such an operation that also look nice and don't incur terrible overhead?
Remember, that, in case of arrays, sizeof(array) returns actual size (aka length) of it's parameter, if it has been declared as an array of explicit size (and you wrote 'series of const char*'s of unknown size'). So, assuming you want to create universal solution, strlen() should come under consideration instead.
I don't think you can avoid all additional operations. In case of many concatenations, the best solution would be to allocate buffer, that is large enough to store all concatenated strings.
We can easily deduce, that the most optimal version of append() in this case is:
string& append (const char* s, size_t n);
Why? Because reference says: 'If s does not point to an array long enough (...), it causes undefined behavior'. So we can assume, that internally no additional checks take place (especially additional strlen() calls). Which is good, since you are completely sure, that values passed to append() are correct and you can avoid unnecesary overhead.
Now, the actual concatenation can be done like this:
len_1 = strlen(array_1);
len_2 = strlen(array_2);
len_3 = strlen(array_3);
//Preallocate enough space for all arrays. Only one reallocation takes place.
target_string.reserve(len_1 + len_2 + len_3);
target_string.append(array_1, len_1);
target_string.append(array_2, len_2);
target_string.append(array_3, len_3);
I do not know if this solution 'looks good' in your opinion, but it's definitely clear and is optimized for this use case.
It seems that std::string - because it doesn't use expression templates - has a O(n^2) complexity instead of a possibly O(n) complexity for some operations like concatenation. Same thing with a std::stringstream class when you have to insert many elements.
I would like to understand this, at least if someone could have some good links about this point that would be great.
Concatenating multiple strings together has different complexities in C++ depending how it is done. I believe the situation that you're thinking of is:
string result = string("Hello, ") + username + "! " +
"Welcome to " + software_product + ".";
which concatenates 6 strings. The first string is copied 5 times, the second is copied 4 times, and so forth. As Leonid Volnitsky notes in his answer, the exact bound for this Θ(NM), where M is the number of concatenation operations, and N is the total length of the strings being concatenated. We can also call this O(N^2) when M <= N. Note that it's not guaranteed that M <= N, because you can increase M without increasing N by trying to concatenate the empty string.
Expression templates could help speed up this use case, though it would cause problems with auto and decltype type inference in C++11, as well as with template type inference in C++98. All of these would deduce the type of
auto result = string("Hello, ") + username + "! " +
"Welcome to " + software_product + ".";
to be the lazily-evaluated string template type that was used to make the expression template magic happen. Other reasons why expression templates are not a great idea include Leonid Volnitsky's answer about this slowing compilation time. It would probably also increase the size of your compiled binary.
Instead, there are other solutions in C++ could be used to get Θ(N) concatenation:
string result = "Hello, ";
result += username;
result += "! ";
result += "Welcome to ";
result += software_product;
result += ".";
In this version, the string is modified in place, and while data that has already been copied into result sometimes needs to be recopied, C++ strings are typically implemented as dynamic arrays that allocate new space exponentially, so that the insertion of each new character takes amortized constant time, leading to overall Θ(N) behavior for repeated concatenation.
The following is a way of doing the same thing, almost on one line. It uses the same principle internally, but also supports converting non-string types to strings using << overloading.
stringstream result;
result << "Hello, " << username << "! " << "Welcome to " << software_product
<< ".";
// do something with result.str()
Lastly, the C++ standard library doesn't include this, but one could define the following function with some stringstream magic inside it. The implementation is left as an exercise for the reader.
template <typename... Items>
std::string concat(std::string const& a, std::string const& b, Items&&... args)
You can then call concat for repeated concatenation on one line in O(N) time:
string result = concat("Hello, ", username, "! ", "Welcome to ",
software_product, ".");
Presumably, all of these are better solutions than messing up type inference by creating an expression template type.
If we have total length of strings expression N, and M concatenation operations then complexity should be O(NM).
It is definitely possible to speed up it up with expression templates. It was not done probably because of their complexity. Compilation speed would be slower too - it will need M-recursive types.
I find hard to believe that a concatenation would go up to O(n^2), but here goes some answer related to your question:
The C++ standard doesn't specify implementation details, and only
specifies complexity requirements in some cases. The only complexity
requirements on std::string operations are that size(), max_size(),
operator[], swap(), c_str() and data() are all constant time. The
complexity of anything else depends on the choices made by whoever
implemented the library you're using.
See reference: C++ string::find complexity
How can it be O(N^2)? String concatenation is:
reallocation if necessary (constant amortized time);
finding the end of the first string (constant time, since they are counted strings);
character copy from the second string (O(N)).
I don't see how templates can have anything to do with this.
I'm finding standard string addition to be very slow so I'm looking for some tips/hacks that can speed up some code I have.
My code is basically structured as follows:
inline void add_to_string(string data, string &added_data) {
if(added_data.length()<1) added_data = added_data + "{";
added_data = added_data+data;
}
int main()
{
int some_int = 100;
float some_float = 100.0;
string some_string = "test";
string added_data;
added_data.reserve(1000*64);
for(int ii=0;ii<1000;ii++)
{
//variables manipulated here
some_int = ii;
some_float += ii;
some_string.assign(ii%20,'A');
//then we concatenate the strings!
stringstream fragment;
fragment<<some_int <<","<<some_float<<","<<some_string;
add_to_string(fragment.str(),added_data);
}
return;
}
Doing some basic profiling, I'm finding that a ton of time is being used in the for loop. Are there some things I can do that will significantly speed this up? Will it help to use c strings instead of c++ strings?
String addition is not the problem you are facing. std::stringstream is known to be slow due to it's design. On every iteration of your for-loop the stringstream is responsible for at least 2 allocations and 2 deletions. The cost of each of these 4 operations is likely more than that of the string addition.
Profile the following and measure the difference:
std::string stringBuffer;
for(int ii=0;ii<1000;ii++)
{
//variables manipulated here
some_int = ii;
some_float += ii;
some_string.assign(ii%20,'A');
//then we concatenate the strings!
char buffer[128];
sprintf(buffer, "%i,%f,%s",some_int,some_float,some_string.c_str());
stringBuffer = buffer;
add_to_string(stringBuffer ,added_data);
}
Ideally, replace sprintf with _snprintf or the equivalent supported by your compiler.
As a rule of thumb, use stringstream for formatting by default and switch to the faster and less safe functions like sprintf, itoa, etc. whenever performance matters.
Edit: that, and what didierc said: added_data += data;
You can save lots of string operations if you do not call add_to_string in your loop.
I believe this does the same (although I am not a C++ expert and do not know exactly what stringstream does):
stringstream fragment;
for(int ii=0;ii<1000;ii++)
{
//variables manipulated here
some_int = ii;
some_float += ii;
some_string.assign(ii%20,'A');
//then we concatenate the strings!
fragment<<some_int<<","<<some_float<<","<<some_string;
}
// inlined add_to_string call without the if-statement ;)
added_data = "{" + fragment.str();
I see you used the reserve method on added_data, which should help by avoiding multiple reallocations of the string as it grows.
You should also use the += string operator where possible:
added_data += data;
I think that the above should save up some significant time by avoiding unecessary copies back and forth of added_data in a temporary string when doing the catenation.
This += operator is a simpler version of the string::append method, it just copies data directly at the end of added_data. Since you made the reserve, that operation alone should be very fast (almost equivalent to a strcpy).
But why going through all this, when you are already using a stringstream to handle input? Keep it all in there to begin with!
The stringstream class is indeed not very efficient.
You may have a look at the stringstream class for more information on how to use it, if necessary, but your solution of using a string as a buffer seems to avoid that class speed issue.
At any rate, stay away from any attempt at reimplementing the speed critical code in pure C unless you really know what you are doing. Some other SO posts support the idea of doing it,, but I think it's best (read safer) to rely as much as possible on the standard library, which will be enhanced over time, and take care of many corner cases you (or I) wouldn't think of. If your input data format is set in stone, then you might start thinking about taking that road, but otherwise it's premature optimization.
If you start added_data with a "{", you would be able to remove the if from your add_to_string method: the if gets executed exactly once, when the string is empty, so you might as well make it non-empty right away.
In addition, your add_to_string makes a copy of the data; this is not necessary, because it does not get modified. Accepting the data by const reference should speed things up for you.
Finally, changing your added_data from string to sstream should let you append to it in a loop, without the sstream intermediary that gets created, copied, and thrown away on each iteration of the loop.
Please have a look at Twine used in LLVM.
A Twine is a kind of rope, it represents a concatenated string using a
binary-tree, where the string is the preorder of the nodes. Since the
Twine can be efficiently rendered into a buffer when its result is used,
it avoids the cost of generating temporary values for intermediate string
results -- particularly in cases when the Twine result is never
required. By explicitly tracking the type of leaf nodes, we can also avoid
the creation of temporary strings for conversions operations (such as
appending an integer to a string).
It may helpful in solving your problem.
How about this approach?
This is a DevPartner for MSVC 2010 report.
string newstring = stringA & stringB;
i dont think strings are slow, its the conversions that can make it slow
and maybe your compiler that might check variable types for mismatches.
My Question is very simple, how is getline(istream, string) implemented?
How can you solve the problem of having fixed size char arrays like with getline (char* s, streamsize n ) ?
Are they using temporary buffers and many calls to new char[length] or another neat structure?
getline(istream&, string&) is implemented in a way that it reads a line. There is no definitive implementation for it; each library probably differs from one another.
Possible implementation:
istream& getline(istream& stream, string& str)
{
char ch;
str.clear();
while (stream.get(ch) && ch != '\n')
str.push_back(ch);
return stream;
}
#SethCarnegie is right: more than one implementation is possible. The C++ standard does not say which should be used.
However, the question is still interesting. It's a classic computer-science problem. Where, and how, does one allocate memory when one does not know in advance how much memory to allocate?
One solution is to record the string's characters as a linked list of individual characters. This is neither memory-efficient nor fast, but it works, is robust, and is relatively simple to program. However, a standard library is unlikely to be implemented this way.
A second solution is to allocate a buffer of some fixed length, such as 128 characters. When the buffer overflows, you allocate a new buffer of double length, 256 characters, then copy the old characters over to the new storage, then release the old. When the new buffer overflows, you allocate an even newer buffer of double length again, 512 characters, then repeat the process; and so on.
A third solution combines the first two. A linked list of character arrays is maintained. The first two members of the list store (say) 128 characters each. The third stores 256. The fourth stores 512, and so on. This requires more programming than the others, but may be preferable to either, depending on the application.
And the list of possible implementations goes on.
Regarding standard-library implementations, #SteveJessop adds that "[a] standard library's string isn't permitted to be implemented as (1), because of the complexity requirement of operator[] for strings. In C++11 it's not permitted to be implemented as (3) either, because of the contiguity requirement for strings. The C++ committee expressed the belief that no active C++ implementation did (3) at the time they added the contiguity requirement. Of course, getline can do what it likes temporarily with the characters before adding them all to the string, but the standard does say a lot about what string can do."
The addition is relevant because, although getline could temporarily store its data in any of several ways, if the data's ultimate target is a string, this may be relevant to getline's implementation. #SteveJessop further adds, "For string itself, implementations are pretty much required to be (2) except that they can choose their own rate of expansion; they don't have to double each time as long as they multiply by some constant."
As #3bdalla said, implementation of thb doesn't work as gnu implementation. So, I wrote my own implementation, which works like gnu's one. I don't know what will be with errors in this variant, so it needs to be tested.
My implementation of getline:
std::istream& getline(std::istream& is, std::string& s, char delim = '\n'){
s.clear();
char c;
std::string temp;
if(is.get(c)){
temp.push_back(c);
while((is.get(c)) && (c != delim))
temp.push_back(c);
if(!is.bad())
s = temp;
if(!is.bad() && is.eof())
is.clear(std::ios_base::eofbit);
}
return is;
}