patterncppMajor
Converting array of bytes to the hex string representation
Viewed 0 times
thearrayhexbytesconvertingrepresentationstring
Problem
Is it possible to make the
hexStr function faster?#include
#include
#include
std::string hexStr(unsigned char* data, int len)
{
std::stringstream ss;
ss << std::hex;
for(int i=0;i<len;++i)
ss << std::setw(2) << std::setfill('0') << (int)data[i];
return ss.str();
}
int main()
{
std::string packet = "";
packet += 0x6b;
packet += 0x0f;
std::string hex = hexStr((unsigned char*)packet.c_str(), packet.length());
printf("%s\n", hex.c_str()); // 6b0f
return 0;
}Solution
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:
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
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
Small benchmark to test this on (bad quality) pseudo-random 300 char long strings:
Sample output compiled with clang++ (3.5)
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 > 4];
s[2 * i + 1] = hexmap[data[i] & 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
#include
#include
#include
#include
std::string hexStr(unsigned char *data, int len)
{
std::stringstream ss;
ss > 4];
s[2 * i + 1] = hexmap[data[i] & 0x0F];
}
return s;
}
int main()
{
srand(time(NULL));
std::vector data;
for (int i = 0; i (rand() % 256);
data.push_back(packet);
}
for (int i = 0; i < 10; i++) {
clock_t beg = clock();
for (auto const &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 &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.001239Code Snippets
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 < len; ++i) {
s[2 * i] = hexmap[(data[i] & 0xF0) >> 4];
s[2 * i + 1] = hexmap[data[i] & 0x0F];
}
return s;
}#include <sstream>
#include <iomanip>
#include <stdio.h>
#include <ctime>
#include <vector>
std::string hexStr(unsigned char *data, int len)
{
std::stringstream ss;
ss << std::hex;
for (int i = 0; i < len; ++i)
ss << std::setw(2) << std::setfill('0') << (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 < len; ++i) {
s[2 * i] = hexmap[(data[i] & 0xF0) >> 4];
s[2 * i + 1] = hexmap[data[i] & 0x0F];
}
return s;
}
int main()
{
srand(time(NULL));
std::vector<std::string> data;
for (int i = 0; i < 1000; i++) {
std::string packet;
for (int i = 0; i < 300; i++)
packet += static_cast<char>(rand() % 256);
data.push_back(packet);
}
for (int i = 0; i < 10; i++) {
clock_t beg = clock();
for (auto const &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 &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));
}
}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.001239Context
StackExchange Code Review Q#78535, answer score: 32
Revisions (0)
No revisions yet.