- 死神を恐れないで
- 速い車線での生活
- 自分の道を行け。パート1。スタック
- 自分の道を行け。パート2。ヒープ
これは、GCのシリーズの3番目です。最初の記事では、Dガベージコレクターとそれを必要とする言語機能を紹介し、効果的に使用するための簡単な手法についても触れました。 2番目の記事では、コード内の特定の場所でGCを制限する言語とライブラリーに存在するツールとライブラリー、およびコンパイラーがそれを行う価値のある場所を特定するのにどのように役立つかを示し、また、Dでプログラムを作成するときは、最初に大胆にGCを使用することをお勧めしました単純な戦略でパフォーマンスへの影響を最小限に抑え、コードを微調整してGCを回避するか、プロファイラーが保証する場所でのみ使用をさらに最適化します。
ガベージコレクターが無効化されているGC.disable
か、関数属性によって無効化されている場合@nogc
でも、メモリをどこかに割り当てる必要があります。また、GCを最大限に使用する場合でも、GCを介したメモリ割り当ての量と数を最小限に抑えることをお勧めします。これは、スタックまたは通常のヒープのいずれかにメモリを割り当てることを意味します。この記事では前者に焦点を当てます。ヒープ上のメモリ割り当てについては、次の記事で取り上げます。
スタックへのメモリの割り当て
Dでの最も単純なメモリ割り当て戦略はCと同じです。ヒープの使用を避け、スタックをできるだけ使用します。配列が必要で、そのサイズがコンパイル時にわかっている場合は、動的配列ではなく静的配列を使用します。構造体には値のセマンティクスがあり、デフォルトでスタックに作成されますが、クラスには参照セマンティクスがあり、通常はヒープに作成されます。可能な限り構造を優先する必要があります。コンパイル時のD機能は、他の方法では不可能な多くのことを実現するのに役立ちます。
静的配列
Dの静的配列では、コンパイル時にサイズがわかっている必要があります。
// OK
int[10] nums;
// : x
int x = 10;
int[x] err;
動的配列とは異なり、リテラルを介して静的配列を初期化しても、GCを介してメモリは割り当てられません。配列とリテラルの長さが一致している必要があります。一致していないと、コンパイラによってエラーが生成されます。
@nogc void main() {
int[3] nums = [1, 2, 3];
}
, , , .
void printNums(int[] nums) {
import std.stdio : writeln;
writeln(nums);
}
void main() {
int[] dnums = [0, 1, 2];
int[3] snums = [0, 1, 2];
printNums(dnums);
printNums(snums);
}
-vgc
, , — . :
int[] foo() {
auto nums = [0, 1, 2];
// - nums...
return nums;
}
nums
. , , . , .
, - , , . , . .dup
:
int[] foo() {
int[3] nums = [0, 1, 2];
// x — - nums
bool condition = x;
if(condition) return nums.dup;
else return [];
}
GC .dup
, . , []
null
— , ( length
) 0, ptr
null
.
D , . , , : .
struct Foo {
int x;
~this() {
import std.stdio;
writefln("#%s says bye!", x);
}
}
void main() {
Foo f1 = Foo(1);
Foo f2 = Foo(2);
Foo f3 = Foo(3);
}
, :
#3 says bye!
#2 says bye!
#1 says bye!
, , . GC new
, GC . , . [std.typecons.scoped](https://dlang.org/phobos/std_typecons.html#.scoped)
.
class Foo {
int x;
this(int x) {
this.x = x;
}
~this() {
import std.stdio;
writefln("#%s says bye!", x);
}
}
void main() {
import std.typecons : scoped;
auto f1 = scoped!Foo(1);
auto f2 = scoped!Foo(2);
auto f3 = scoped!Foo(3);
}
, , . core.object.destroy
, .
, scoped
, destroy
@nogc
-. , , GC, , @nogc
-. , nogc
, .
, . (Plain Old Data, POD) , - GUI, , . , , . , , , .
alloca
C D « », alloca
. , GC, . , :
import core.stdc.stdlib : alloca;
void main() {
size_t size = 10;
void* mem = alloca(size);
// Slice the memory block
int[] arr = cast(int[])mem[0 .. size];
}
C, alloca
: . , arr
. arr.dup
.
, Queue
, «». D , . Java , , . D , (Design by Introspection). , , , , UFCS, ( ).
DbI . (. .)
Queue
. , , . , : - . , , , .
// `Size` 0
// ;
//
struct Queue(T, size_t Size = 0)
{
// .
// `public` DbI-
// , Queue .
enum isFixedSize = Size > 0;
void enqueue(T item)
{
static if(isFixedSize) {
assert(_itemCount < _items.length);
}
else {
ensureCapacity();
}
push(item);
}
T dequeue() {
assert(_itemCount != 0);
static if(isFixedSize) {
return pop();
}
else {
auto ret = pop();
ensurePacked();
return ret;
}
}
//
static if(!isFixedSize) {
void reserve(size_t capacity) {
/* */
}
}
private:
static if(isFixedSize) {
T[Size] _items;
}
else T[] _items;
size_t _head, _tail;
size_t _itemCount;
void push(T item) {
/* item, _head _tail */
static if(isFixedSize) { ... }
else { ... }
}
T pop() {
/* item, _head _tail */
static if(isFixedSize) { ... }
else { ... }
}
//
static if(!isFixedSize) {
void ensureCapacity() { /* , */ }
void ensurePacked() { /* , */}
}
}
:
Queue!Foo qUnbounded;
Queue!(Foo, 128) qBounded;
qBounded
. qUnbounded, . , . isFixedSize
:
void doSomethingWithQueueInterface(T)(T queue)
{
static if(T.isFixedSize) { ... }
else { ... }
}
: __traits(hasMember, T, "reserve")
, — : hasMember!T("reserve")
. __traits
std.traits
— DbI; .
GC. , , — GC .
シリーズの次の記事では、GCを経由せずに通常のヒープにメモリを割り当てる方法について説明します。