【发布时间】:2016-03-11 12:18:19
【问题描述】:
我编写了代码来测量forward_list 的排序性能,但与直觉相反,似乎使用内存池分配器实际上会使其变慢:
// clang++ -O3 -std=c++11 -I/usr/local/include -lboost_system sort-test.cc -o sort-test
#include <boost/pool/pool_alloc.hpp>
#include <algorithm>
#include <cmath>
#include <chrono>
#include <forward_list>
#include <fstream>
#include <iostream>
#include <vector>
using namespace std;
using namespace std::chrono;
using namespace boost;
class TSampler {
public:
void AddSample(double value) {
NumSamples++;
if (NumSamples == 1) {
Mean = value;
M = value;
M2 = value * value;
return;
}
double dillutionFactor = 1.0 / NumSamples;
// variance recurrence
double cm = value - M;
M += dillutionFactor * cm;
M2 += cm * (value - M);
// mean recurrence
Mean += dillutionFactor * (value - Mean);
}
double GetMean() const {
return Mean;
}
double GetStdDev() const {
Recalc();
return StdDev;
}
double GetConfidenceRadius() const {
Recalc();
return Radius;
}
double GetConfidenceLo() const {
Recalc();
return Mean - Radius;
}
double GetConfidenceHi() const {
Recalc();
return Mean + Radius;
}
private:
void Recalc() const {
StdDev = sqrt(M2 / (NumSamples - 1.0));
Radius = 1.96 * StdDev / sqrt(NumSamples + 0.0);
}
private:
size_t NumSamples = 0;
double Mean = NAN, M = NAN, M2 = NAN;
mutable double StdDev = NAN, Radius = NAN;
};
void FillSamples(int *samples, size_t size) {
ifstream r("/dev/urandom");
r.read(reinterpret_cast<char *>(samples), size * sizeof(*samples));
}
using TList = forward_list<int>;
using TPooledList = forward_list<int, fast_pool_allocator<int>>;
using TVector = vector<int>;
void InitContainer(TList &xs, int *input, size_t size) {
for (int i = size - 1; i >= 0; --i) {
xs.push_front(input[i]);
}
}
void InitContainer(TPooledList &xs, int *input, size_t size) {
for (int i = size - 1; i >= 0; --i) {
xs.push_front(input[i]);
}
}
void InitContainer(TVector &xs, int *input, size_t size) {
xs.reserve(size);
copy(input, input + size, back_inserter(xs));
}
void Sort(TPooledList &xs) {
xs.sort();
}
void Sort(TList &xs) {
xs.sort();
}
void Sort(TVector &xs) {
sort(xs.begin(), xs.end());
}
template<typename TContainer>
void RunSort(TSampler &sampler, int *input, size_t size) {
TContainer xs;
InitContainer(xs, input, size);
steady_clock::time_point before = steady_clock::now();
Sort(xs);
steady_clock::time_point after = steady_clock::now();
auto delta = duration_cast<duration<double>>(after - before);
sampler.AddSample(delta.count());
}
int main(int argc, char **argv) {
argc--; argv++;
if (argc != 2) {
cerr << "Usage: sort-test T N\nT: number of trials\nN: size of the vector/list" << endl;
exit(1);
}
int t = atoi(argv[0]);
int n = atoi(argv[1]);
if (t <= 0 || n <= 0) {
cerr << "Invalid arguments" << endl;
exit(1);
}
TSampler listSampler;
TSampler pooledListSampler;
TSampler vectorSampler;
TVector input;
input.resize(n);
for (int trial = 0; trial < t; ++trial) {
FillSamples(&input[0], n);
RunSort<TList>(listSampler, &input[0], n);
RunSort<TPooledList>(pooledListSampler, &input[0], n);
RunSort<TVector>(vectorSampler, &input[0], n);
}
cout << "List: " << listSampler.GetMean()
<< " ± " << listSampler.GetConfidenceRadius()
<< " (95% confidence)" << endl;
cout << "Pooled list: " << pooledListSampler.GetMean()
<< " ± " << pooledListSampler.GetConfidenceRadius()
<< " (95% confidence)" << endl;
cout << "Vector: " << vectorSampler.GetMean()
<< " ± " << vectorSampler.GetConfidenceRadius()
<< " (95% confidence)" << endl;
}
在我的 2013 MacBook Air 上:
[03:04:59 dev]$ ./sort-test 10 10000
List: 0.00082735 ± 0.000189312 (95% confidence)
Pooled list: 0.00091835 ± 0.000201482 (95% confidence)
Vector: 0.000484268 ± 0.000107911 (95% confidence)
[03:05:03 dev]$ ./sort-test 10 10000
List: 0.000764033 ± 0.000188491 (95% confidence)
Pooled list: 0.00103925 ± 0.00038322 (95% confidence)
Vector: 0.000482046 ± 0.000111277 (95% confidence)
[03:05:08 dev]$ ./sort-test 30 10000
List: 0.000747641 ± 6.93474e-05 (95% confidence)
Pooled list: 0.000912401 ± 9.43897e-05 (95% confidence)
Vector: 0.000526488 ± 9.34585e-05 (95% confidence)
[03:05:13 dev]$ ./sort-test 30 10000
List: 0.000810124 ± 8.71386e-05 (95% confidence)
Pooled list: 0.000946477 ± 0.000115813 (95% confidence)
Vector: 0.000495523 ± 5.44617e-05 (95% confidence)
[03:05:18 dev]$ ./sort-test 30 100000
List: 0.0107134 ± 0.00110686 (95% confidence)
Pooled list: 0.018314 ± 0.00102368 (95% confidence)
Vector: 0.00583989 ± 0.000514794 (95% confidence)
为什么会这样?一个原因可能是使用内存池时内存局部性实际上更差,因为列表节点是使用普通的operator ::new 分配的,但是我不得不问如何正确使用fast_pool_allocator。
【问题讨论】:
-
有工具可以调查。想到 Cachegrind 或 Intel VTune。
-
这些数字实际上是什么意思?
-
@sehe 可以看到代码,它实现了en.wikipedia.org/wiki/Algorithms_for_calculating_variance
标签: c++ memory-management boost allocator