Увеличение производительности выделения для строк

Я портировал тестовую программу Java GC на C ++ (см. Код ниже), а также на Python. Производительность Java и Python намного выше, чем в C ++, и я подумал, что это связано со всеми обращениями к new это должно быть сделано для создания строк каждый раз. Я пытался использовать Boost’s fast_pool_allocator но это фактически ухудшило производительность с 700 до 1200 мс. Я неправильно использую распределитель, или я должен делать что-то еще?

РЕДАКТИРОВАТЬ: Составлено с g++ -O3 -march=native --std=c++11 garbage.cpp -lboost_system, g ++ является версией 4.8.1
Одна итерация занимает в Python около 300 мс, а с Java около 50 мс. std::allocator дает около 700 мс и boost::fast_pool_allocator дает около 1200мс.

#include <string>
#include <vector>
#include <chrono>
#include <list>
#include <iostream>
#include <boost/pool/pool_alloc.hpp>
#include <memory>
//#include <gc/gc_allocator.h>using namespace std;
#include <sstream>
typedef boost::fast_pool_allocator<char> c_allocator;
//typedef std::allocator<char> c_allocator;
typedef basic_string<char, char_traits<char>, c_allocator> pool_string;
namespace patch {
template <typename T> pool_string to_string(const T& in) {
std::basic_stringstream<char, char_traits<char>, c_allocator> stm;
stm << in;
return stm.str();
}
}#include "mytime.hpp"
class Garbage {
public:
vector<pool_string> outer;
vector<pool_string> old;
const int nThreads = 1;
//static auto time = chrono::high_resolution_clock();

void go() {
//        outer.resize(1000000);
//old.reserve(1000000);
auto tt = mytime::msecs();
for (int i = 0; i < 10; ++i) {
if (i % 100 == 0) {
cout << "DOING AN OLD" << endl;
doOld();
tt = mytime::msecs();
}

for (int j = 0; j < 1000000/nThreads; ++j)
outer.push_back(patch::to_string(j));

outer.clear();
auto t = mytime::msecs();
cout << (t - tt) << endl;
tt = t;
}
}

void doOld() {
old.clear();
for (int i = 0; i < 1000000/nThreads; ++i)
old.push_back(patch::to_string(i));
}
};

int main() {
Garbage().go();
}

3

Решение

Проблема в том, что вы каждый раз используете новый поток строк для преобразования целого числа.

Почини это:

namespace patch {
template <typename T> pool_string to_string(const T& in) {
return boost::lexical_cast<pool_string>(in);
}
}

Теперь время:

DOING AN OLD
0.175462
0.0670085
0.0669926
0.0687969
0.0692518
0.0669318
0.0669196
0.0669187
0.0668962
0.0669185

real    0m0.801s
user    0m0.784s
sys 0m0.016s

Видеть это Жить на Колиру

Полный код для справки:

#include <boost/pool/pool_alloc.hpp>
#include <chrono>
#include <iostream>
#include <list>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include <boost/lexical_cast.hpp>
//#include <gc/gc_allocator.h>

using string = std::string;

namespace patch {
template <typename T> string to_string(const T& in) {
return boost::lexical_cast<string>(in);
}
}

class Timer
{
typedef std::chrono::high_resolution_clock clock;
clock::time_point _start;
public:
Timer() { reset(); }
void reset() { _start = now(); }
double elapsed()
{
using namespace std::chrono;
auto e = now() - _start;
return duration_cast<nanoseconds>(e).count()*1.0e-9;
}
clock::time_point now()
{
return clock::now();
}
};class Garbage {
public:
std::vector<string> outer;
std::vector<string> old;
const int nThreads = 1;

void go() {
outer.resize(1000000);
//old.reserve(1000000);
Timer timer;

for (int i = 0; i < 10; ++i) {
if (i % 100 == 0) {
std::cout << "DOING AN OLD" << std::endl;
doOld();
}

for (int j = 0; j < 1000000/nThreads; ++j)
outer.push_back(patch::to_string(j));

outer.clear();
std::cout << timer.elapsed() << std::endl;
timer.reset();
}
}

void doOld() {
old.clear();
for (int i = 0; i < 1000000/nThreads; ++i)
old.push_back(patch::to_string(i));
}
};

int main() {
Garbage().go();
}
5

Другие решения

Так как я не использую boost на моей машине, я упростил код для использования стандарта C ++ 11 to_string (таким образом случайно «исправляя» проблему, которую он нашел), и получил это:

#include <string>
#include <vector>
#include <chrono>
#include <list>
#include <iostream>
#include <memory>
//#include <gc/gc_allocator.h>
#include <sstream>
using namespace std;class Timer
{
typedef std::chrono::high_resolution_clock clock;
clock::time_point _start;
public:
Timer() { reset(); }
void reset() { _start = now(); }
double elapsed()
{
using namespace std::chrono;
auto e = now() - _start;
return duration_cast<nanoseconds>(e).count()*1.0e-9;
}
clock::time_point now()
{
return clock::now();
}
};class Garbage {
public:
vector<string> outer;
vector<string> old;
const int nThreads = 1;
Timer timer;

void go() {
//        outer.resize(1000000);
//old.reserve(1000000);
for (int i = 0; i < 10; ++i) {
if (i % 100 == 0) {
cout << "DOING AN OLD" << endl;
doOld();
}

for (int j = 0; j < 1000000/nThreads; ++j)
outer.push_back(to_string(j));

outer.clear();
cout << timer.elapsed() << endl;
timer.reset();
}
}

void doOld() {
old.clear();
for (int i = 0; i < 1000000/nThreads; ++i)
old.push_back(to_string(i));
}
};

int main() {
Garbage().go();
}

Компилирование с:

$ g++ -O3 -std=c++11 gc.cpp
$ ./a.out
DOING AN OLD
0.414637
0.189082
0.189143
0.186336
0.184449
0.18504
0.186302
0.186055
0.183123
0.186835

Сборка clang 3.5 с исходным кодом от пятницы 18 апреля 2014 года дает аналогичные результаты с теми же параметрами компилятора.

Мой процессор — AMD Phenom ™ II X4 965, работающий на частоте 3,6 ГГц (если я правильно помню).

2

По вопросам рекламы [email protected]