Poco::MemoryPool を紹介します。
Poco::MemoryPool は、固定サイズのメモリブロックのプールで、メモリブロックのアロケーションの高速化が主な目的です。アロケートされたメモリブロックは、利用されるまで retain されます。メモリブロックの最大数が指定でき、プリアロケートしておくこともできます。
MemoryPoolTest.cpp
・TestBasics() で基本的な動作を例示。
・256byte のブロックを 4 つ、プリアロケートせずに確保。
・1 ブロックずつ allocate/release/allocate し、allocated() と available() を表示。
・TestSpeed() で動作速度を測定。
・256byte のブロックを、最大 1024×1024 個確保。(256MBytes)
・new/delete と Poco::MemoryPool のプリアロケート無し、有りの 3 者を比較。
・MeasureAllocDeallocTime というテンプレートクラスを作り、Init(), Cleanup(), Alloc(), Dealloc()
を Poco::MemoryPool に対するテンプレートの特殊化で対応。
・new/delete の時は、テンプレートに Poco::Void を渡して対応。
・allocate/deallocate(release) を 3 回繰り返し、それぞれにかかった時間と、初期化/終了時/トータル
の所要時間を測定。
#include <Poco/Format.h> #include <Poco/MemoryPool.h> #include <Poco/Stopwatch.h> #include <Poco/Void.h> #include <string> #include "ScopedLogMessage.h" #include "PrepareConsoleLogger.h" const std::size_t kBlockSize = 256; const int kNumMaxAlloc_Basics = 4; const int kNumMaxAlloc_Speed = 1024*1024; const std::size_t kNumLoop = 3; typedef struct { double timeTotal; double timeInit; double timeCleanup; double timeAlloc[kNumLoop]; double timeDealloc[kNumLoop]; } ElapsedTimeRec; void ShowMemoryPoolAttr(ScopedLogMessage& msg, const Poco::MemoryPool& pool) { msg.Message(Poco::format(" blockSize()=%z, allocated()=%d, available()=%d", pool.blockSize(), pool.allocated(), pool.available())); } void TestBasics(ScopedLogMessage& msg) { msg.Message("--- TestBasics ---"); Poco::MemoryPool pool(kBlockSize, 0, kNumMaxAlloc_Basics); msg.Message(Poco::format(" Poco::MemoryPool(%z, %d, %d)", kBlockSize, 0, kNumMaxAlloc_Basics)); ShowMemoryPoolAttr(msg, pool); std::vector<void*> ptrs; msg.Message(" pool.get()"); for(int i=0; i<kNumMaxAlloc_Basics; ++i) { ptrs.push_back(pool.get()); ShowMemoryPoolAttr(msg, pool); } try { pool.get(); } catch(Poco::OutOfMemoryException&) { msg.Message(Poco::format(" calling pool.get() more than %d fails", kNumMaxAlloc_Basics)); } msg.Message(" pool.release()"); for(int i=0; i<kNumMaxAlloc_Basics; ++i) { pool.release(ptrs[i]); ShowMemoryPoolAttr(msg, pool); } msg.Message(" pool.get() again"); for(int i=0; i<kNumMaxAlloc_Basics; ++i) { ptrs.push_back(pool.get()); ShowMemoryPoolAttr(msg, pool); } } template <class T> class MeasureAllocDeallocTime { public: MeasureAllocDeallocTime(ElapsedTimeRec& timeRec, std::size_t blockSize, int preAlloc, int maxAlloc) : m_pPool (NULL) { std::vector<char*> ptrs(maxAlloc); { Poco::Stopwatch sw_total; sw_total.start(); { // initialize Poco::Stopwatch sw_init; sw_init.start(); Init(blockSize, preAlloc, maxAlloc); sw_init.stop(); timeRec.timeInit = (1000.0 * sw_init.elapsed()) / sw_init.resolution(); } for(std::size_t loop=0; loop<kNumLoop; ++loop) { { // allocate Poco::Stopwatch sw_alloc; sw_alloc.start(); for(int i=0; i<maxAlloc; ++i) { ptrs[i] = Alloc(blockSize); } sw_alloc.stop(); timeRec.timeAlloc[loop] = (1000.0 * sw_alloc.elapsed()) / sw_alloc.resolution(); } { // deallocate Poco::Stopwatch sw_dealloc; sw_dealloc.start(); for(int i=0; i<maxAlloc; ++i) { Dealloc(ptrs[i]); } sw_dealloc.stop(); timeRec.timeDealloc[loop] = (1000.0 * sw_dealloc.elapsed()) / sw_dealloc.resolution(); } } { // cleanup Poco::Stopwatch sw_cleanup; sw_cleanup.start(); Cleanup(); sw_cleanup.stop(); timeRec.timeCleanup = (1000.0 * sw_cleanup.elapsed()) / sw_cleanup.resolution(); } sw_total.stop(); timeRec.timeTotal = (1000.0 * sw_total.elapsed()) / sw_total.resolution(); } } ~MeasureAllocDeallocTime() { } private: MeasureAllocDeallocTime() : m_pPool (NULL) { } void Init(std::size_t /*blockSize*/, int /*preAlloc*/, int /*maxAlloc*/) { } void Cleanup(void) { } char* Alloc(std::size_t blockSize) { return new char[blockSize]; } void Dealloc(char* ptr) { delete [] ptr; } T* m_pPool; }; template <> void MeasureAllocDeallocTime<Poco::MemoryPool>::Init(std::size_t blockSize, int preAlloc, int maxAlloc) { m_pPool = new Poco::MemoryPool(blockSize, preAlloc, maxAlloc); } template <> void MeasureAllocDeallocTime<Poco::MemoryPool>::Cleanup(void) { if(NULL != m_pPool) { delete m_pPool; m_pPool = NULL; } } template <> char* MeasureAllocDeallocTime<Poco::MemoryPool>::Alloc(std::size_t /*blockSize*/) { return static_cast<char*>(m_pPool->get()); } template <> void MeasureAllocDeallocTime<Poco::MemoryPool>::Dealloc(char* ptr) { m_pPool->release(ptr); } #if defined(POCO_OS_FAMILY_WINDOWS) #include <new.h> int ThrowBadAlloc(std::size_t) { throw std::bad_alloc(); return 0; } #endif void TestSpeed(ScopedLogMessage& msg) { ElapsedTimeRec timeRec[3]; msg.Message("--- TestSpeed ---"); int numMaxAlloc = kNumMaxAlloc_Speed; { // adjust number of max allocation for low memory machine bool memoryAvailable = false; char* tempPtr = NULL; #if defined(POCO_OS_FAMILY_WINDOWS) _set_new_handler(ThrowBadAlloc); #endif while(!memoryAvailable) { try { tempPtr = new char [kBlockSize * numMaxAlloc]; memoryAvailable = true; } catch(...) { numMaxAlloc /= 2; } delete [] tempPtr; tempPtr = NULL; } msg.Message(Poco::format(" number of allocation blocks = %d", numMaxAlloc)); } { msg.Message(" measuring new/delete..."); MeasureAllocDeallocTime<Poco::Void> measure(timeRec[0], kBlockSize, 0, numMaxAlloc); } { msg.Message(Poco::format(" measuring Poco::MemoryPool(%z, %d, %d)..." , kBlockSize , 0 , numMaxAlloc)); MeasureAllocDeallocTime<Poco::MemoryPool> measure(timeRec[1], kBlockSize, 0, numMaxAlloc); } { msg.Message(Poco::format(" measuring Poco::MemoryPool(%z, %d, %d)..." , kBlockSize , numMaxAlloc , numMaxAlloc)); MeasureAllocDeallocTime<Poco::MemoryPool> measure(timeRec[2], kBlockSize, numMaxAlloc, numMaxAlloc); } msg.Message(" new/delete non pre-allocated pre-allocated"); msg.Message("================================================================"); msg.Message(Poco::format(" init %8.3fmSec %8.3fmSec %8.3fmSec" , timeRec[0].timeInit , timeRec[1].timeInit , timeRec[2].timeInit)); for(std::size_t loop=0; loop<kNumLoop; ++loop) { msg.Message(Poco::format(" allocate #%z %8.3fmSec %8.3fmSec %8.3fmSec" , loop+1 , timeRec[0].timeAlloc[loop] , timeRec[1].timeAlloc[loop] , timeRec[2].timeAlloc[loop])); msg.Message(Poco::format("deallocate #%z %8.3fmSec %8.3fmSec %8.3fmSec" , loop+1 , timeRec[0].timeDealloc[loop] , timeRec[1].timeDealloc[loop] , timeRec[2].timeDealloc[loop])); } msg.Message(Poco::format(" cleanup %8.3fmSec %8.3fmSec %8.3fmSec" , timeRec[0].timeCleanup , timeRec[1].timeCleanup , timeRec[2].timeCleanup)); msg.Message("================================================================"); msg.Message(Poco::format(" total time %8.3fmSec %8.3fmSec %8.3fmSec" , timeRec[0].timeTotal , timeRec[1].timeTotal , timeRec[2].timeTotal)); } int main(int /*argc*/, char** /*argv*/) { PrepareConsoleLogger logger(Poco::Logger::ROOT, Poco::Message::PRIO_INFORMATION); ScopedLogMessage msg("MemoryPoolTest ", "start", "end"); TestBasics(msg); TestSpeed(msg); return 0; } |
Results of execution
・一旦 get() して release() するまで、available() が(直感的に)正しい値を返さないことに注意。
「新たにアロケートすることなく使える個数」くらいの意味。
・トータルの所要時間は、new/delete > non pre-allocated > pre-allocated だが、non pre-allocated と
pre-allocated との差が実使用状態であるかどうかは微妙なところ。
[0] MemoryPoolTest start [0] --- TestBasics --- [0] Poco::MemoryPool(256, 0, 4) [0] blockSize()=256, allocated()=0, available()=0 [0] pool.get() [0] blockSize()=256, allocated()=1, available()=0 [0] blockSize()=256, allocated()=2, available()=0 [0] blockSize()=256, allocated()=3, available()=0 [0] blockSize()=256, allocated()=4, available()=0 [0] calling pool.get() more than 4 fails [0] pool.release() [0] blockSize()=256, allocated()=4, available()=1 [0] blockSize()=256, allocated()=4, available()=2 [0] blockSize()=256, allocated()=4, available()=3 [0] blockSize()=256, allocated()=4, available()=4 [0] pool.get() again [0] blockSize()=256, allocated()=4, available()=3 [0] blockSize()=256, allocated()=4, available()=2 [0] blockSize()=256, allocated()=4, available()=1 [0] blockSize()=256, allocated()=4, available()=0 [0] --- TestSpeed --- [0] number of allocation blocks = 1048576 [0] measuring new/delete... [0] measuring Poco::MemoryPool(256, 0, 1048576)... [0] measuring Poco::MemoryPool(256, 1048576, 1048576)... [0] new/delete non pre-allocated pre-allocated [0] ================================================================ [0] init 0.001mSec 0.015mSec 86.591mSec [0] allocate #1 84.544mSec 121.834mSec 40.697mSec [0] deallocate #1 499.517mSec 64.868mSec 39.726mSec [0] allocate #2 83.324mSec 40.682mSec 40.683mSec [0] deallocate #2 495.804mSec 39.706mSec 39.716mSec [0] allocate #3 83.412mSec 40.692mSec 40.680mSec [0] deallocate #3 504.934mSec 39.738mSec 39.743mSec [0] cleanup 0.000mSec 499.142mSec 486.371mSec [0] ================================================================ [0] total time 1751.557mSec 846.687mSec 814.214mSec [0] MemoryPoolTest end |
Downloads
・ここをクリックすると、makefile や VC++ プロジェクトなど一式がダウンロードできます。
(2013.05.31 updated)
・2010年6月14日からのダウンロード数:1081
Subversion
・フリーの Subversion ホスティングサービス Assemblaで、ソースコードを管理しています。
Reference
・http://pocoproject.org にある MemoryManagement のプレセンテーション。(PDF)
![]() |
Copyright © 2010 Round Square Inc. All rights reserved. |
---|
0 Comments.