Boost.Poolでメモリ確保に要する時間について

Boost.Poolを使ってメモリを確保するのにどのくらいの時間がかかるか調べてみた。
常に一定時間で、かつ、newよりも高速にメモリを確保出来るかどうか知りたかったからだ。
実際に動かして確認してみたところ、Boost.Poolは大きめのサイズのメモリをプールとして確保して
そこからメモリを小分けに使えるようにしてくれるのだが、
プールの残量が足りなくなるともう一度大きなプールを確保するように動作した。
このためプール不足時に大きな遅延が発生してしまう。


メモリ確保にかかる時間は、100bytesのメモリを確保する前後で時刻を取得し、その差分を取る事で求めた。
計測時にはこれを10万回繰り返し行った。
また計測用プログラムの実行時にリアルタイム優先度99、スケジューリングポリシーにFIFOを指定した。
計測したOSはFedora15でlibc最適化オプションはO2。


結果は以下のようになった。
左側がnewで計測したもので、右側がboost::object_poolを使ったもの。
x軸が実行番号でy軸がメモリ確保に要した時間で単位はsecだ。
上段と下段はそれぞれ同じ計測結果でy軸の値の範囲が違うだけである。

Boostの方がほとんどの場合には速いのだが、定期的にミリ秒単位の時間がかかっているのがわかると思う。


ゲームとかだとメモリを高速に確保するためにプールを使うかもしれないが、
そういう場合にBoost.Poolは使えないと思う。定期的に大きな遅延が発生するためだ。
だからと言ってBoost.Poolに他の使い道があるようにも思えないのでこれをどこで使うのかよくわからない。


ちなみにnewは0x21000bytesまではかなり高速にメモリを確保できるのがわかった。
こんなコードをstraceにかけると

#include <stdlib.h>
#include <stdio.h>
int main()
{
	printf("hello\n");
	int *p = new int;
	p = new int;
	p = new int;
	return 0;
}

・・・略
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe13e0c0000
write(1, "hello\n", 6hello
) = 6
brk(0) = 0x22b1000
brk(0x22d2000) = 0x22d2000
brk(0) = 0x22d2000
exit_group(0) = ?

のようになり、最初にnewをすると0x21000bytes分先に確保することがわかる。
これはmalloc()でやっても同様だった。


自分で要求に合うメモリプールを作る場合にはこの本が参考になるかもしれません。

Efficient C++パフォーマンスプログラミングテクニック

Efficient C++パフォーマンスプログラミングテクニック

  • 作者: ダブブルカ,デビットメイヒュ,浜田光之,Dov Bulka,David Mayhew,浜田真理
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/07
  • メディア: 単行本
  • 購入: 9人 クリック: 149回
  • この商品を含むブログ (31件) を見る

今回使ったプログラムも掲載しておきます。
Makefile

.PHONY: all
all: test
test: test.o timer.o
	g++ $^ -o $@ -lrt

test.o: test.cpp
	g++ -Wall -g -c -O2 $^

timer.o:timer.cpp
	g++ -Wall -g -c -O2 $^

.PHONY: run
run:
	sudo chrt -f 99 ./test

.PHONY: clean
clean:
	find ./ -name "*.log" -exec rm -f {} \;
	find ./ -name "*.o" -exec rm {} \;
	find ./ -name "test" -exec rm {} \;

test.cpp

#include <boost/array.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/object_pool.hpp>
#include <fstream>
#include <string>
#include <iostream>

#include <stdio.h>
#include <stdlib.h>

#include "timer.hpp"

template <int Size>
struct Data
{
   char data[Size];
};

template <class T, class U>
void pool_test(U& array)
{
   boost::pool<> p(sizeof(T));
   for(unsigned int i = 0; i < array.size(); ++i) {
      timespec t = now_time();
      p.malloc();
      array[i] = duration(now_time(), t);
   }
}

template <class T, class U>
void object_pool_test(U& array)
{
   boost::object_pool<T> p;
   for(unsigned int i = 0; i < array.size(); ++i) {
      timespec t = now_time();
      p.construct();
      array[i] = duration(now_time(), t);
   }
}


template <class T, class U>
void malloc_test(U& array)
{
   for(unsigned int i = 0; i < array.size(); ++i) {
      timespec t = now_time();
      malloc(sizeof(T));
      array[i] = duration(now_time(), t);
   }
}

template <class T, class U>
void new_test(U& array)
{
   for(unsigned int i = 0; i < array.size(); ++i) {
      timespec t = now_time();
      new T;
      array[i] = duration(now_time(), t);
   }
}

template <class T, int times>
void heap_test(const std::string testname)
{
   std::cout << testname << " " << sizeof(T) << std::endl;
   {
      std::string funcname("pool");
      std::string subname(testname + "." + funcname + ".log");
      boost::array<timespec, times> array;
      pool_test<T>(array);
      std::ofstream ofs(subname.c_str());
      for(unsigned int i = 0; i < array.size(); ++i) {
         ofs << tostring(array[i]).c_str() << "\n";
      }
   }

   std::cout << testname << " " << sizeof(T) << std::endl;
   {
      std::string funcname("object_pool");
      std::string subname(testname + "." + funcname + ".log");
      boost::array<timespec, times> array;
      object_pool_test<T>(array);
      std::ofstream ofs(subname.c_str());
      for(unsigned int i = 0; i < array.size(); ++i) {
         ofs << tostring(array[i]).c_str() << "\n";
      }
   }


   {
      std::string funcname("new");
      std::string subname(testname + "." + funcname + ".log");
      boost::array<timespec, times> array;
      new_test<T>(array);
      std::ofstream ofs(subname.c_str());
      for(unsigned int i = 0; i < array.size(); ++i) {
         ofs << tostring(array[i]).c_str() << "\n";
      }
   }

   {
      std::string funcname("malloc");
      std::string subname(testname + "." + funcname + ".log");
      boost::array<timespec, times> array;
      malloc_test<T>(array);
      std::ofstream ofs(subname.c_str());
      for(unsigned int i = 0; i < array.size(); ++i) {
         ofs << tostring(array[i]).c_str() << "\n";
      }
   }
}

int main()
{
   heap_test<int,       100000>("int");
   heap_test<Data<10>,  100000>("10byte");
   heap_test<Data<50>,  100000>("50byte");
   heap_test<Data<100>, 100000>("100byte");

   return 0;
}

timer.hpp

#ifndef __MY_TIMER_HPP__
#define __MY_TIMER_HPP__

#include <time.h>
#include <string>

timespec now_time();
timespec duration(const timespec& lhs, const timespec& rhs);
void print_timespec(const timespec& t);
std::string tostring(const timespec& t);

#endif

timer.cpp

#include "timer.hpp"
#include <cassert>
#include <stdio.h>

timespec now_time()
{
   timespec t;
   clock_gettime(CLOCK_MONOTONIC, &t);
   return t;
}

timespec duration(const timespec& lhs, const timespec& rhs)
{
   time_t sec = lhs.tv_sec - rhs.tv_sec;
   if(sec < 0) {
      struct timespec t = {0};
      return t;
   }

   long nano = lhs.tv_nsec - rhs.tv_nsec;
   if(nano < 0) {
      --sec;
      nano = 1000000000 + nano;
   }
   struct timespec t = {sec, nano};
   return t;
}

void print_timespec(const timespec& t)
{
   printf("%ld.%09ld\n", t.tv_sec, t.tv_nsec);
}

std::string tostring(const timespec& t)
{
   char buf[BUFSIZ] = {0};
   sprintf(buf,"%ld.%09ld", t.tv_sec, t.tv_nsec);
   return std::string(buf);
}

#ifdef __MAIN__

int main()
{
   timespec t0 = { 1, 1000};
   timespec t1 = { 2,    0};
   timespec d0 = duration(t1, t0);
   assert(d0.tv_sec == 0);
   assert(d0.tv_nsec == 999999000);
   for(int i = 0; i < 1000000; ++i) {
   	timespec n0 = now_time();
   	timespec n1 = now_time();
   	print_timespec(duration(n1,n0));
   }
   return 0;
}

#endif
#!/usr/bin/env python

import sys
import matplotlib.pyplot as plt

def average(floats):
	return reduce(lambda x, y: x + y, floats) / len(floats)
def sample_variarance(floats):
	avrg = average(floats)
	return reduce(lambda x, y: x+ y, [(d -avrg)** 2.0 for d in floats])

datalist = [[float(line) for line in open(filename)] for filename in sys.argv[1:]]
ymax = max([max(data) for data in datalist]) + 0.0001

i = 0
for data in datalist:
	i = i + 1
	plt.subplot(200 + len(datalist) * 10 + i)
	plt.axis([0.0, len(data), 0.0, ymax])
	plt.title(sys.argv[i])
	print "%s\tavrg %.10f"%(sys.argv[i], average(data))
	#plt.ylabel("[sec]")
	plt.plot(range(0,len(data)), data)

j = 0
for data in datalist:
	i = i + 1
	j = j + 1
	plt.subplot(200 + len(datalist) * 10 + i)
	plt.axis([0.0, len(data), 0.0, 0.00001])
	#plt.title(sys.argv[j])
	#plt.ylabel("[sec]")
	plt.plot(range(0,len(data)), data, 'bo')

plt.show()