Multi-threaded performance std::string
<p>We are running some code on a project that uses OpenMP and I've run into something strange. I've included parts of some play code that demonstrates what I see.</p>

<p>The tests compare calling a function with a const char* argument with a std::string argument in a multi-threaded loop. The functions essentially do nothing and so have no overhead. </p>

<p>What I do see is a major difference in the time it takes to complete the loops. For the const char* version doing 100,000,000 iterations the code takes 0.075 seconds to complete compared with 5.08 seconds for the std::string version. These tests were done on Ubuntu-10.04-x64 with gcc-4.4.</p>

<p>My question is basically whether this is solely due the dynamic allocation of std::string and why in this case that can't be optimized away since it is const and can't change?</p>

<p>Code below and many thanks for your responses. </p>

<p>Compiled with: g++ -Wall -Wextra -O3 -fopenmp string_args.cpp -o string_args</p>

<pre><code>#include &lt;iostream&gt;
#include &lt;map&gt;
#include &lt;string&gt;
#include &lt;stdint.h&gt;

// For wall time
#ifdef _WIN32
#include &lt;time.h&gt;
#else
#include &lt;sys/time.h&gt;
#endif

namespace
{
  const int64_t g_max_iter = 100000000;
  std::map&lt;const char*, int&gt; g_charIndex = std::map&lt;const char*,int&gt;();
  std::map&lt;std::string, int&gt; g_strIndex = std::map&lt;std::string,int&gt;();

  class Timer
  {
  public:
    Timer()
    {
    #ifdef _WIN32
      m_start = clock();
    #else /* linux &amp; mac */
      gettimeofday(&amp;m_start,0);
    #endif
    }

    float elapsed()
    {
    #ifdef _WIN32
      clock_t now = clock();
      const float retval = float(now - m_start)/CLOCKS_PER_SEC;
      m_start = now;
    #else /* linux &amp; mac */
      timeval now;
      gettimeofday(&amp;now,0);
      const float retval = float(now.tv_sec - m_start.tv_sec) + float((now.tv_usec - m_start.tv_usec)/1E6);
      m_start = now;
    #endif
      return retval;
    }

  private:
    // The type of this variable is different depending on the platform
#ifdef _WIN32
    clock_t
#else
    timeval
#endif
    m_start;   ///&lt; The starting time (implementation dependent format)
  };

}

bool contains_char(const char * id)
{
  if( g_charIndex.empty() ) return false;
  return (g_charIndex.find(id) != g_charIndex.end());
}

bool contains_str(const std::string &amp; name)
{
  if( g_strIndex.empty() ) return false;
  return (g_strIndex.find(name) != g_strIndex.end());
}

void do_serial_char()
{
  int found(0);
  Timer clock;
  for( int64_t i = 0; i &lt; g_max_iter; ++i )
  {
    if( contains_char("pos") )
    {
     ++found;
    }
  }
  std::cout &lt;&lt; "Loop time: " &lt;&lt; clock.elapsed() &lt;&lt; "\n";
  ++found;
}

void do_parallel_char()
{
  int found(0);
  Timer clock;
#pragma omp parallel for
  for( int64_t i = 0; i &lt; g_max_iter; ++i )
  {
    if( contains_char("pos") )
    {
     ++found;
    }
  }
  std::cout &lt;&lt; "Loop time: " &lt;&lt; clock.elapsed() &lt;&lt; "\n";
  ++found;
}

void do_serial_str()
{
  int found(0);
  Timer clock;
  for( int64_t i = 0; i &lt; g_max_iter; ++i )
  {
    if( contains_str("pos") )
    {
     ++found;
    }
  }
  std::cout &lt;&lt; "Loop time: " &lt;&lt; clock.elapsed() &lt;&lt; "\n";
  ++found;
}

void do_parallel_str()
{
  int found(0);
  Timer clock;
#pragma omp parallel for
  for( int64_t i = 0; i &lt; g_max_iter ; ++i )
  {
    if( contains_str("pos") )
    {
     ++found;
    }
  }
  std::cout &lt;&lt; "Loop time: " &lt;&lt; clock.elapsed() &lt;&lt; "\n";
  ++found;
}

int main()
{
  std::cout &lt;&lt; "Starting single-threaded loop using std::string\n";
  do_serial_str();
  std::cout &lt;&lt; "\nStarting multi-threaded loop using std::string\n";
  do_parallel_str();

  std::cout &lt;&lt; "\nStarting single-threaded loop using char *\n";
  do_serial_char();
  std::cout &lt;&lt; "\nStarting multi-threaded loop using const char*\n";
  do_parallel_char();
  }
</code></pre>
