Performance while creating a string

Hello,

I have a program where I need to generate and send a string if an event occurs.

My initial code was something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
std::string label = "some constant text"

...

std::string message = "Event detected by " + label;
// an event has an integer that increments and a const char* 
message = message + ", :" + std::to_string(event_number) + "\t" + std::string(event_source);

... open a socket

BytesSent = send(SendingSocket, message.c_str(), strlen(message.c_str()), 0);

... close socket


As you can see initially I was not very concerned about performance and opted to bring everything to std::string and concatenate those. It turns out the events are rather frequent and as a result I am doing this very often, what leads to high CPU usage. So now I would like to try to lower the CPU usage by making this more efficient.

The first thing I changed was to open the socket and keep the connection for the next message. But now I would like to improve the efficiency of the message generation and I would like to ask for your advice on how to best do this.

I could stick with the std::string, but I could go to std::stringstream, I could use a char buffer instead of a std::string, of implement something like sprintf and probably there are even more alternatives possible. [strong]The main question is, what would be the most efficient way and why?[/strong]

Kind regards, Nico
I cannot comment on the "most efficient", but:


You could use reserve() and append() on the 'message'.

The reserve() to make sure that the message has sufficient capacity to store the complete text rather than do reallocations.
1
2
3
message = message + ", :";  // creates unnamed temporary string object
// vs
message.append( ", :");


1
2
3
4
strlen(message.c_str()) // isn't this a loop?

// how about
message.size() // might need a +1 
> It turns out the events are rather frequent and as a result I am doing this very often, what leads to high CPU usage.
> So now I would like to try to lower the CPU usage by making this more efficient.

It is extremely unlikely that formatting the messages is what is taking a significant portion of cpu time.

As far as performance goes, one measurement is worth more than a thousand opinions.
Measure the time taken to format messages on your implementation; consider low-level optimisation if and only if it turns out to be a bottleneck.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <string>
#include <random>
#include <ctime>
#include <cmath>

struct event // simulated random event
{
     std::size_t event_number = ++number ;
     std::string temp = "source #" + std::to_string( rng() % 16 ) ;
     const char* event_source = temp.c_str() ;
     
     static int number ;
     static std::mt19937 rng ;
};

int event::number = 0 ;
std::mt19937 event::rng ; // deliberately not seeded

int main()
{
    const std::size_t N = 800'000 ;
    static const event random_events[N] {} ;

    unsigned int r = 0 ;

    {
        static const std::string label = "some constant text" ;
        static const std::string message_prefix = "message detected by " + label ;
        static const std::string sep = ", :" ;
        static const std::string tab = "\t" ;

        const auto start = std::clock() ;
        {
            for( const event& ev : random_events )
            {
                static std::string message ; // static: so that we don't have to construct and destroy 
                               // a new string each time (the optimiser may not be fiendishly clever)

                message = message_prefix + sep + std::to_string( ev.event_number ) + tab + ev.event_source ;
                // send message (not timed)
                r += message.size() ;
            }
        }
        const auto end = std::clock() ;

        const double msecs = (end-start) * 1000.0 / CLOCKS_PER_SEC ;
        std::cout << "formatted " << std::round( N/msecs )  << " messages per processor millisecond\n" ;
    }

    return r != 0 ;
}

echo && echo '--------- clang++/libc++ -------' && clang++ -std=c++14 -stdlib=libc++ -O3 -Wall -Wextra -pedantic-errors  main.cpp -lsupc++ && ./a.out
echo && echo '---------- g++/libstdc++ --------' && g++ -std=c++14 -O3 -Wall -Wextra -pedantic-errors  main.cpp && ./a.out
echo && echo && uname -a && dmesg | grep processor

--------- clang++/libc++ -------
formatted 2286 messages per processor millisecond

---------- g++/libstdc++ --------
formatted 2286 messages per processor millisecond


Linux stacked-crooked 3.2.0-74-virtual #109-Ubuntu SMP Tue Dec 9 17:04:48 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
[    0.000000] Detected 2992.516 MHz processor.

http://coliru.stacked-crooked.com/a/4c5148bae2a61451
Topic archived. No new replies allowed.