6

I'm creating a game in which I have a main loop. During one cycle of this loop, I have to convert int value to string about ~50-100 times. So far I've been using this function:

std::string Util::intToString(int val)
{
   std::ostringstream s;
   s << val;
   return s.str();
}

But it doesn't seem to be quite efficient as I've encountered FPS drop from ~120 (without using this function) to ~95 (while using it).

Is there any other way to convert int to string that would be much more efficient than my function?

Piotr Chojnacki
  • 6,837
  • 5
  • 34
  • 65
  • 9
    [`std::to_string`](http://en.cppreference.com/w/cpp/string/basic_string/to_string)? – Some programmer dude Feb 02 '13 at 11:33
  • 2
    Can you show the loop? It may be possible to optimize it before seeking to convert ints to strings efficiently. – StoryTeller - Unslander Monica Feb 02 '13 at 11:34
  • 2
    What is the range of your ints? Do you need to deal with negatives? – Sergey Kalinichenko Feb 02 '13 at 11:36
  • There's no way that calling this function 100 times per cycle is killing your performance so badly. – Puppy Feb 02 '13 at 11:37
  • 1
    @JoachimPileborg That should be an answer... I wasn't aware there's a function like this in standard library. I already checked it and I have around 117 FPS while using this function, so almost no difference. Thanks! – Piotr Chojnacki Feb 02 '13 at 11:39
  • @StoryTeller I'm afraid it's a bit too extended to show it all here. – Piotr Chojnacki Feb 02 '13 at 11:39
  • First of all, time if it is really the conversion that costs you time. If it really is, you can try the following (but measure exactly if it really helps in you case): avoid creating a new stream for each conversion - use an old-fashioned sprintf(), perhaps you can also use the same char[] buffer each time instead of creating a new std::string (depends on your concurrency requirements). Important point is how often your loop is run - do you perform 50-100 conversions once per frame or once per second (in latter case I doubt it could be the cause of your slowdown)? – Michał Kosmulski Feb 02 '13 at 11:39
  • @dasblinkenlight It's 1-72 range. I don't have to deal with negatives. – Piotr Chojnacki Feb 02 '13 at 11:40
  • Did you try [itoa](http://www.cplusplus.com/reference/cstdlib/itoa/)? – Sergey Kalinichenko Feb 02 '13 at 11:40
  • 2
    @Mosquito If the range is so small, you can create an array of 72 pre-formatted strings and index it with your int value - it will be very fast. – Michał Kosmulski Feb 02 '13 at 11:42
  • You all say that this function shouldn't be a reason for that slowdown, but actually it seemed to be a problem, because when I commented it out, FPS number was 20+ higher. `std::to_string()` function helped a lot. – Piotr Chojnacki Feb 02 '13 at 11:45
  • std::ostringstream ctor...... – Martin James Feb 02 '13 at 13:13
  • Your function has to constantly create the stringstream, which I hear is expensive. Why not have a static stringstream to use for all your conversions. –  Apr 26 '19 at 03:54

5 Answers5

9

It's 1-72 range. I don't have to deal with negatives.

Pre-create an array/vector of 73 string objects, and use an index to get your string. Returning a const reference will let you save on allocations/deallocations, too:

// Initialize smallNumbers to strings "0", "1", "2", ...
static vector<string> smallNumbers;

const string& smallIntToString(unsigned int val) {
    return smallNumbers[val < smallNumbers.size() ? val : 0];
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
4

The standard std::to_string function might be a useful.

However, in this case I'm wondering if maybe it's not the copying of the string when returning it might be as big a bottleneck? If so you could pass the destination string as a reference argument to the function instead. However, if you have std::to_string then the compiler probably is C++11 compatible and can use move semantics instead of copying.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • RVO should take care of the copy issue. However the user code show many different objects and thus probably has several buffer allocations; I would be that `std::to_string` is much more optimized in this regard. – Matthieu M. Feb 02 '13 at 14:28
3

Yep — fall back on functions from C, as explored in this previous answer:

namespace boost {
template<>
inline std::string lexical_cast(const int& arg)
{
    char buffer[65]; // large enough for arg < 2^200
    ltoa( arg, buffer, 10 );
    return std::string( buffer ); // RVO will take place here
}
}//namespace boost

In theory, this new specialisation will take effect throughout the rest of the Translation Unit in which you defined it. ltoa is much faster (despite being non-standard) than constructing and using a stringstream.

However, I've experienced problems with name conflicts between instantiations of this specialisation, and instantiations of the original function template, between competing shared libraries.

In order to get around that, I actually just give this function a whole new name entirely:

template <typename T>
inline std::string fast_lexical_cast(const T& arg)
{
    return boost::lexical_cast<std::string>(arg);
}

template <>
inline std::string my_fast_lexical_cast(const int& arg)
{
    char buffer[65];

    if (!ltoa(arg, buffer, 10)) {
       boost::throw_exception(boost::bad_lexical_cast(
          typeid(std::string), typeid(int)
       ));
    }

    return std::string(buffer);
}

Usage: std::string myString = fast_lexical_cast<std::string>(42);

Disclaimer: this modification is reverse-engineered from Kirill's original SO code, not the version that I created and put into production from my company codebase. I can't think right now, though, of any other significant modifications that I made to it.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
1

Something like this:

 const int size = 12;
 char buf[size+1];
 buf[size] = 0;
 int index = size;
 bool neg = false
 if (val < 0) {    // Obviously don't need this if val is always positive.
    neg = true;
    val = -val;
 }

 do
 {
     buf[--index] = (val % 10) + '0';
     val /= 10;
 } while(val);
 if (neg)
 {
    buf[--index] = '-';
 }
 return std::string(&buf[index]);
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
1

I use this:

void append_uint_to_str(string & s, unsigned int i)
{
    if(i > 9)
        append_uint_to_str(s, i / 10);
    s += '0' + i % 10;
}

If You want negative insert:

if(i < 0)
{
    s += '-';
    i = -i;
}

at the beginning of function.

Greg
  • 11
  • 1