# メモリ割り当てしたい(`G4Allocator`) ```cpp // 書式: G4ThreadLocal G4Allocator* allocator; // 例: SensorHit型のアロケーター // include/SensorHit.hh で宣言する extern G4ThreadLocal G4Allocator* SensorHitAllocator; // src/SensorHit.cc で定義する(nullptrで実体化) G4ThreadLocal G4Allocator* SensorHitAllocator = nullptr; ``` `G4Allocator`は、テンプレートクラスであり、 型`T`のオブジェクトに特化した高速なメモリ管理ユーティリティです。 このクラスは、高頻度で生成・破棄されるオブジェクトのメモリ確保・解放の効率を最適化する **メモリプール型のアロケーター** であり、 `G4Step`や`G4Track`の処理などGeant4内部でも利用されています。 :::{note} 通常の`new` / `delete`演算子は、頻繁なメモリ確保・解放によりオーバーヘッドが大きくなります。 `G4Allocator`では、あらかじめ確保したメモリブロックを使い回すことで、このオーバーヘッドを大幅に軽減します。 ::: ユーザーが明示的に利用する場面は、前述した[ユーザー定義のヒットクラス](./geant4-sensor-hit.md)です。 対象のユーザー定義クラスで `operator new()` および `operator delete()` をオーバーロードし、 その内部で`MallocSingle()` / `FreeSingle()` を呼び出す必要があります。 また、マルチスレッド環境では、`G4Allocator`をスレッドごとに分離するために、 `G4ThreadLocal`を併用します。 ## メモリを割り当てたい(`MallocSingle`) ```cpp SensorHitAllocator->MallocSingle(); ``` `MallocSingle`は、`G4Allocator`が管理しているメモリプールから、T型(ここでは`SensorHit`型)オブジェクト1つ分のメモリを割り当てる関数です。 これはC++標準の`malloc`に相当します。 通常は、以下のように `new`演算子(`operator new`)をオーバーロードして使います。 ```cpp void* SensorHit::operator new(size_t) { if(!SensorHitAllocator) { SensorHitAllocator = new G4Allocator; } return (void *) SensorHitAllocator->MallocSingle(); }; ``` `operator new()`は、コンストラクターが呼ばれるまえに実行される特殊関数です。 ここで`SensorHitAllocator`を初期化し、必要なメモリを確保しています。 ## メモリを解放したい(`FreeSingle`) ```cpp SensorHitAllocator->FreeSingle(型名) ``` `FreeSingle`は、`MallocSingle`で割り当てたメモリを再びメモリプールに返す関数です。 C++標準の`free`に相当します。 通常は、`new`演算子(`operator delete`)をオーバーロードして使います。 ```cpp void SensorHit::operator delete(void *hit) { SensorHitAllocator->FreeSingle((SensorHit*)hit); }; ``` `operator delete()`演算子は、デストラクターの後に実行される特殊関数です。 前述の`new`演算子で割り当てたメモリを、ここでアロケーターに返却しています。 ## ヘッダーファイル(`include/SensorHit.hh`) ```cpp #include "G4VHit.hh" #include "G4Allocator.hh" class SensorHit : public G4VHit { public: void* operator new(size_t); void operator delete(void*); // その他のメンバー変数や関数 }; ////////////////////////////////////////////////// // (スレッドローカルな)グローバル変数を宣言 // 1. スレッドごとにSensorHit型のメモリを管理するためのアロケータ // 2. そのメモリ領域を SensorHitAllocator という名前で管理する // 3. 実体の定義は .cc ファイルに記述する extern G4ThreadLocal G4Allocator* SensorHitAllocator; ``` `G4Allocator`はユーザー定義のヒットクラスと同じファイルで宣言します。 `SensorHitAllocator`のようにグローバルに定義することで、 どこからでも`MallocSingl()e`、`FreeSingle()`を呼び出して効率的にメモリを管理できます。 ```cpp extern G4ThreadLocal G4Allocator* SensorHitAllocator; ``` Geant4はマルチスレッド環境がデフォルトになっているため、 各スレッドが独立してアロケーターを保持できるよに`G4ThreadLocal`を併用します。 また、C++ではグローバル変数を複数のファイルから参照する場合、 `extern`を使って宣言する必要があります。 ## ソースファイル(`src/SensorHit.cc`) ```cpp #include "SensorHit.hh" G4ThreadLocal G4Allocator* SensorHitAllocator = nullptr; void* SensorHit::operator new(size_t) { // new演算子でメモリを割り当てるとき // { // SensorHit *hit = new SensorHit(); // } // 1. SensorHitAllocatorに割り当てられたメモリのポインターを取得する // 2. 初期化されてない場合は、新しくG4Allocatorオブジェクトを作成 if(!SensorHitAllocator) { SensorHitAllocator = new G4Allocator; } return (void *) SensorHitAllocator->MallocSingle(); }; ////////////////////////////////////////////////// void SensorHit::operator delete(void *hit) { // delete演算子でメモリを解放するとき // // { // SensorHit *hit = new SensorHit(); // delete hit // } // // 1. SensorHitAllocatorに割り当てられたメモリのポインターを解放する SensorHitAllocator->FreeSingle((SensorHit*)hit); }; ``` `G4Allocator`クラスはGeant4に用意されている高速なメモリアロケーターです。 C++のメモリ管理に詳しくないひとは、とりあえず使っておけばよいと思います。 ## 割り当てサイズをしりたい(``GetAllocatedSize``) ```cpp // 割り当てられた合計サイズ G4int size = SensorHitAllocator->GetAllocatedSize(); // 現在のページのサイズ G4int page_size = SensorHitAllocator->GetPageSize(); // 割り当てられたページ数 G4int n_pages = SensorHitAllocator->GetNoPages(); ``` ``GetAllocatedSize``で、割り当てられたメモリの合計サイズを確認できます。 ## リファレンス - [G4Allocator](https://geant4.kek.jp/Reference/11.2.0/classG4Allocator.html)