C++ 提高字符串的分配性能
我将java GC测试程序移植到C++(参见下面的代码)以及Python。java和python的性能远远大于C++,我认为这是因为每次调用<代码>新< /COD>每次都要创建字符串。我曾尝试使用Boost的C++ 提高字符串的分配性能,c++,linux,memory-management,boost,memory-pool,C++,Linux,Memory Management,Boost,Memory Pool,我将java GC测试程序移植到C++(参见下面的代码)以及Python。java和python的性能远远大于C++,我认为这是因为每次调用新< /COD>每次都要创建字符串。我曾尝试使用Boost的快速池分配器,但这实际上使性能从700ms恶化到1200ms。我使用分配器是错误的,还是我应该做些别的事情 编辑:使用g++-O3-march=native--std=c++11 garbage.cpp-lboost\u系统编译。g++是4.8.1版 Python的一次迭代大约需要300毫秒,Jav
快速池分配器
,但这实际上使性能从700ms恶化到1200ms。我使用分配器是错误的,还是我应该做些别的事情
编辑:使用g++-O3-march=native--std=c++11 garbage.cpp-lboost\u系统编译。g++是4.8.1版
Python的一次迭代大约需要300毫秒,Java的一次迭代大约需要50毫秒std::allocator
给出大约700ms,而boost::fast\u pool\u allocator
给出大约1200ms
#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();
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
//#包括
使用名称空间std;
#包括
typedef boost::fast_pool_分配器c_分配器;
//类型定义std::分配器c_分配器;
typedef基本字符串池字符串;
命名空间修补程序{
模板池字符串到字符串(常量T&in){
std::basic_stringstream stm;
stm问题在于每次都使用一个新的字符串流来转换整数
修复它:
namespace patch {
template <typename T> pool_string to_string(const T& in) {
return boost::lexical_cast<pool_string>(in);
}
}
查看它
完整代码供参考:
#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();
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
//#包括
使用string=std::string;
命名空间修补程序{
模板字符串到_字符串(常量T&in){
返回boost::词法转换(in);
}
}
班级计时器
{
typedef std::chrono::高分辨率时钟;
时钟:时间点开始;
公众:
计时器(){reset();}
void reset(){u start=now();}
双时间()
{
使用名称空间std::chrono;
自动e=现在()-\u开始;
返回持续时间_cast(e).count()*1.0e-9;
}
时钟:现在的时间点()
{
返回时钟::现在();
}
};
类垃圾{
公众:
std::向量外部;
std::向量old;
常数int=1;
void go(){
外部。调整大小(1000000);
//旧储备(1000000);
定时器;
对于(int i=0;i<10;++i){
如果(i%100==0){
std::cout因为我的机器上没有使用boost,所以我将代码简化为使用标准C++11来处理字符串(从而意外地“修复”了sehe发现的问题),并得到以下结果:
#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();
}
2014年4月18日星期五发布的Clang3.5BuildwithSource在相同的编译器选项下给出了类似的结果
我的处理器是AMD Phenom(tm)II X4 965,运行频率为3.6GHz(如果我没记错的话)。这似乎表明你确实用错了:池分配器
用于连续块(例如新字符[n]
),快速池分配器
用于单个事物(例如新字符
).谢谢。我刚试过这个,但等它打印一个号码我就烦了(也就是说,花了很长时间)@user315118-在您的帖子中没有提到使用的编译器、在构建应用程序时使用的编译器选项以及优化等。如果您要发布声称在某个时间单位内执行的代码,则必须同时发布您的编译器和使用的选项。否则,我们正确地假设您使用的是旧版本编译器、损坏的编译器或未完全启用优化的编译。你是说每个循环需要0.7-1.2秒吗?在我的机器上,我使用标准分配器大约需要0.19秒。要么你在相当慢的硬件上运行,要么你没有启用优化。@PaulMcKenzie谢谢。我用这个信息更新了这个问题像往常一样,这是“不是你认为它花时间的地方”的问题……这是一个好地方。@Matstpeterson确实如此。像往常一样,这是“profile,profile,profile”(虽然我在这一个中没有使用):)@sehe太好了!我没有考虑stringstream的分配。将其静态删除会得到类似的结果。虽然在我的计算机上,迭代的最佳性能仍然只有175ms左右。我用你的计时器替换了我的计时器,现在大约为100ms。所以对mats Peterson加倍强调。@mats Peterson绝对…我被你的计时器宠坏了我的GCed语言哈哈。我实际上做了一个调用图,但令人钦佩地看了一眼它列出的所有模板功能并关闭了它:/这就像说前轮驱动汽车比后轮驱动汽车快。真正影响速度的不是哪个车轮驱动汽车,而是整体设计、发动机功率,如果转弯是正确的涉及轮胎、悬架等-前轮或后轮驱动在整个方案中是相当小的因素。我相信这个类比与“垃圾收集与非垃圾收集”相比相当好。在我正在进行的编译器项目中,我只是懒散地忽略所有释放的内存,而依赖于在退出时清理整个内容。显然不会永远工作,但是…感谢STL的回答。当我实际编写/调试我正在使用的代码时,cygwin
,它有一个中断的to_string
实现,因此是一个使用stringstreams。是的,不使用windows有它的优点…;)
#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