Converting array of bytes to the hex string representation
Yes, but I doubt you'll see any practical difference with such short input.

Two ideas: reduce the number of possible dynamic allocations, and do the conversion yourself with a small lookup table.

You can do both of these by pre-allocating the string container (you know the target size already), then doing the hex conversion manually. Something like this:

constexpr char hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7',
                           '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

std::string hexStr(unsigned char *data, int len)
{
  std::string s(len * 2, ' ');
  for (int i = 0; i &lt; len; ++i) {
    s[2 * i]     = hexmap[(data[i] &amp; 0xF0) &gt;&gt; 4];
    s[2 * i + 1] = hexmap[data[i] &amp; 0x0F];
  }
  return s;
}


Running this on my machine, with a test string that is 128k times 256 chars (from 0 to 255) gives me about 1.1s for your code, and about 0.18s for mine (redirecting to /dev/null to avoid I/O issues).

This should be at least not-worse than your version for small strings - there's no opportunity for the compiler to make a sub-optimal inlining decision on the string stream operators in particular, and (named) RVO will take care of eliding the copy from the return value to the caller's std::string hex.

Small benchmark to test this on (bad quality) pseudo-random 300 char long strings:

#include &lt;sstream&gt;
#include &lt;iomanip&gt;
#include &lt;stdio.h&gt;
#include &lt;ctime&gt;
#include &lt;vector&gt;

std::string hexStr(unsigned char *data, int len)
{
  std::stringstream ss;
  ss &lt;&lt; std::hex;
  for (int i = 0; i &lt; len; ++i)
    ss &lt;&lt; std::setw(2) &lt;&lt; std::setfill('0') &lt;&lt; (int)data[i];
  return ss.str();
}

constexpr char hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7',
                           '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

std::string hexStr2(unsigned char *data, int len)
{
  std::string s(len * 2, ' ');
  for (int i = 0; i &lt; len; ++i) {
    s[2 * i]     = hexmap[(data[i] &amp; 0xF0) &gt;&gt; 4];
    s[2 * i + 1] = hexmap[data[i] &amp; 0x0F];
  }
  return s;
}

int main()
{
  srand(time(NULL));
  std::vector&lt;std::string&gt; data;

  for (int i = 0; i &lt; 1000; i++) {
    std::string packet;
    for (int i = 0; i &lt; 300; i++)
      packet += static_cast&lt;char&gt;(rand() % 256);
    data.push_back(packet);
  }
  for (int i = 0; i &lt; 10; i++) {
    clock_t beg = clock();
    for (auto const &amp;s : data) {
      std::string hex = hexStr((unsigned char *)s.c_str(), s.length());
      printf("%s\n", hex.c_str());
    }
    fprintf(stderr, "Elapsed 1: %f\n",
            (clock() - beg) / (1.0 * CLOCKS_PER_SEC));
    beg = clock();
    for (auto const &amp;s : data) {
      std::string hex2 = hexStr2((unsigned char *)s.c_str(), s.length());
      printf("%s\n", hex2.c_str());
    }
    fprintf(stderr, "Elapsed 2: %f\n",
            (clock() - beg) / (1.0 * CLOCKS_PER_SEC));
  }
}


Sample output compiled with clang++ (3.5) -std=c++11 -O3 (doesn't vary too much), stdout redirected to /dev/null:

Elapsed 1: 0.018132
Elapsed 2: 0.001365
Elapsed 1: 0.017858
Elapsed 2: 0.001373
Elapsed 1: 0.018429
Elapsed 2: 0.001313
Elapsed 1: 0.018918
Elapsed 2: 0.001229
Elapsed 1: 0.019134
Elapsed 2: 0.001244
Elapsed 1: 0.018915
Elapsed 2: 0.001239
Elapsed 1: 0.018031
Elapsed 2: 0.001266
Elapsed 1: 0.018051
Elapsed 2: 0.001239
Elapsed 1: 0.017071
Elapsed 2: 0.001315
Elapsed 1: 0.017841
Elapsed 2: 0.001239