メモリ割り当てしたい(G4Allocator<T>

1// 書式: G4ThreadLocal G4Allocator<T>* allocator;
2// 例: SensorHit型のアロケーター
3
4// include/SensorHit.hh で宣言する
5extern G4ThreadLocal G4Allocator<SensorHit>* SensorHitAllocator;
6
7// src/SensorHit.cc で定義する(nullptrで実体化)
8G4ThreadLocal G4Allocator<SensorHit>* SensorHitAllocator = nullptr;

G4Allocator<T>は、テンプレートクラスであり、 型Tのオブジェクトに特化した高速なメモリ管理ユーティリティです。

このクラスは、高頻度で生成・破棄されるオブジェクトのメモリ確保・解放の効率を最適化する メモリプール型のアロケーター であり、 G4StepG4Trackの処理などGeant4内部でも利用されています。

注釈

通常のnew / delete演算子は、頻繁なメモリ確保・解放によりオーバーヘッドが大きくなります。 G4Allocatorでは、あらかじめ確保したメモリブロックを使い回すことで、このオーバーヘッドを大幅に軽減します。

ユーザーが明示的に利用する場面は、前述したユーザー定義のヒットクラスです。 対象のユーザー定義クラスで operator new() および operator delete() をオーバーロードし、 その内部でMallocSingle() / FreeSingle() を呼び出す必要があります。

また、マルチスレッド環境では、G4Allocatorをスレッドごとに分離するために、 G4ThreadLocalを併用します。

メモリを割り当てたい(MallocSingle

1SensorHitAllocator->MallocSingle();

MallocSingleは、G4Allocatorが管理しているメモリプールから、T型(ここではSensorHit型)オブジェクト1つ分のメモリを割り当てる関数です。 これはC++標準のmallocに相当します。

通常は、以下のように new演算子(operator new)をオーバーロードして使います。

1void* SensorHit::operator new(size_t)
2{
3    if(!SensorHitAllocator) {
4        SensorHitAllocator = new G4Allocator<SensorHit>;
5    }
6    return (void *) SensorHitAllocator->MallocSingle();
7};

operator new()は、コンストラクターが呼ばれるまえに実行される特殊関数です。 ここでSensorHitAllocatorを初期化し、必要なメモリを確保しています。

メモリを解放したい(FreeSingle

1SensorHitAllocator->FreeSingle(型名)

FreeSingleは、MallocSingleで割り当てたメモリを再びメモリプールに返す関数です。 C++標準のfreeに相当します。

通常は、new演算子(operator delete)をオーバーロードして使います。

1void SensorHit::operator delete(void *hit)
2{
3    SensorHitAllocator->FreeSingle((SensorHit*)hit);
4};

operator delete()演算子は、デストラクターの後に実行される特殊関数です。 前述のnew演算子で割り当てたメモリを、ここでアロケーターに返却しています。

ヘッダーファイル(include/SensorHit.hh

 1#include "G4VHit.hh"
 2#include "G4Allocator.hh"
 3
 4class SensorHit : public G4VHit
 5{
 6  public:
 7    void* operator new(size_t);
 8    void operator delete(void*);
 9    // その他のメンバー変数や関数
10};
11
12//////////////////////////////////////////////////
13// (スレッドローカルな)グローバル変数を宣言
14// 1. スレッドごとにSensorHit型のメモリを管理するためのアロケータ
15// 2. そのメモリ領域を SensorHitAllocator という名前で管理する
16// 3. 実体の定義は .cc ファイルに記述する
17extern G4ThreadLocal G4Allocator<SensorHit>* SensorHitAllocator;

G4Allocatorはユーザー定義のヒットクラスと同じファイルで宣言します。 SensorHitAllocatorのようにグローバルに定義することで、 どこからでもMallocSingl()eFreeSingle()を呼び出して効率的にメモリを管理できます。

1extern G4ThreadLocal G4Allocator<SensorHit>* SensorHitAllocator;

Geant4はマルチスレッド環境がデフォルトになっているため、 各スレッドが独立してアロケーターを保持できるよにG4ThreadLocalを併用します。

また、C++ではグローバル変数を複数のファイルから参照する場合、 externを使って宣言する必要があります。

ソースファイル(src/SensorHit.cc

 1#include "SensorHit.hh"
 2
 3G4ThreadLocal G4Allocator<SensorHit>* SensorHitAllocator = nullptr;
 4
 5void* SensorHit::operator new(size_t)
 6{
 7    // new演算子でメモリを割り当てるとき
 8    // {
 9    //     SensorHit *hit = new SensorHit();
10    // }
11    // 1. SensorHitAllocatorに割り当てられたメモリのポインターを取得する
12    // 2. 初期化されてない場合は、新しくG4Allocator<TrackerHit>オブジェクトを作成
13    if(!SensorHitAllocator) {
14        SensorHitAllocator = new G4Allocator<SensorHit>;
15    }
16    return (void *) SensorHitAllocator->MallocSingle();
17};
18
19
20
21//////////////////////////////////////////////////
22void SensorHit::operator delete(void *hit)
23{
24    // delete演算子でメモリを解放するとき
25    //
26    // {
27    //     SensorHit *hit = new SensorHit();
28    //     delete hit
29    // }
30    //
31    // 1. SensorHitAllocatorに割り当てられたメモリのポインターを解放する
32    SensorHitAllocator->FreeSingle((SensorHit*)hit);
33};

G4AllocatorクラスはGeant4に用意されている高速なメモリアロケーターです。 C++のメモリ管理に詳しくないひとは、とりあえず使っておけばよいと思います。

割り当てサイズをしりたい(GetAllocatedSize

1// 割り当てられた合計サイズ
2G4int size = SensorHitAllocator->GetAllocatedSize();
3
4// 現在のページのサイズ
5G4int page_size = SensorHitAllocator->GetPageSize();
6
7// 割り当てられたページ数
8G4int n_pages = SensorHitAllocator->GetNoPages();

GetAllocatedSizeで、割り当てられたメモリの合計サイズを確認できます。

リファレンス