スライス・アロケータとスライス・メモリー・セグメント
スライス・アロケータは、既存のメモリー・セグメントから取得されたメモリーの連続領域(スライス)を返すことで、割当てリクエストに応答するセグメント・アロケータを返します。また、MethodSegment::asSliceメソッドを使用して、メモリー・セグメント内の任意の場所のメモリー・セグメントのスライスを取得することもできます。
スライス・アロケータ
次の例では、60個のJava int
値を保持できるsegment
という名前のメモリー・セグメントを割り当てます。次に、SegmentAllocator.slicingAllocator(MemorySegment)を呼び出してスライス・アロケータを使用し、segment
から連続して10個のスライスを取得します。この例では、各スライスに5つの整数の配列を割り当てます。その後、各スライスの内容が出力されます。
void allocate60Int() {
try (Arena arena = Arena.ofConfined()) {
SequenceLayout SEQUENCE_LAYOUT =
MemoryLayout.sequenceLayout(60L, ValueLayout.JAVA_INT);
MemorySegment segment = arena.allocate(SEQUENCE_LAYOUT);
SegmentAllocator allocator = SegmentAllocator.slicingAllocator(segment);
MemorySegment s[] = new MemorySegment[10];
for (int i = 0 ; i < 10 ; i++) {
s[i] = allocator.allocateFrom(
ValueLayout.JAVA_INT, 1, 2, 3, 4, 5);
}
for (int i = 0 ; i < 10 ; i++) {
int[] intArray = s[i].toArray(ValueLayout.JAVA_INT);
System.out.println(Arrays.toString(intArray));
}
} catch (Exception e) {
e.printStackTrace();
}
}
セグメント・アロケータを構成要素として使用して、カスタム割当て戦略をサポートするアリーナを作成できます。たとえば、多数のネイティブ・セグメントが同じ制限付き存続期間を共有する場合、カスタム・アリーナはスライス・アロケータを使用してセグメントを効率的に割り当てることができます。これにより、クライアントは(スライスによる)スケーラブルな割当てと(アリーナによる)決定性の割当て解除の両方を利用できます。
次の例では、限定アリーナのように動作するがスライス・アロケータを内部的に使用して割当てリクエストに応答するスライス・アリーナを定義します。スライス・アリーナが閉じられると、基礎となる限定アリーナが閉じられ、スライス・アリーナに割り当てられたすべてのセグメントが無効になります。
この例を簡略化するために、ArenaおよびSegmentAllocator (Arenaのスーパーインタフェース)のメソッドのサブセットのみを実装します。
public class SlicingArena implements Arena {
final Arena arena = Arena.ofConfined();
final SegmentAllocator slicingAllocator;
SlicingArena(MemoryLayout m) {
slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(m));
}
public MemorySegment allocate(long byteSize, long byteAlignment) {
return slicingAllocator.allocate(byteSize, byteAlignment);
}
public MemorySegment.Scope scope() {
return arena.scope();
}
public void close() {
arena.close();
}
}
このスライス・アリーナでは、このセクションの最初の例をより簡潔に書き換えることができます。
void allocate60IntWithSlicingArena() {
SequenceLayout SEQUENCE_LAYOUT =
MemoryLayout.sequenceLayout(60L, ValueLayout.JAVA_INT);
try (Arena slicingArena = new SlicingArena(SEQUENCE_LAYOUT)) {
MemorySegment s[] = new MemorySegment[10];
for (int i = 0 ; i < 10 ; i++) {
s[i] = slicingArena.allocateFrom(
ValueLayout.JAVA_INT, 1, 2, 3, 4, 5);
}
for (int i = 0 ; i < 10 ; i++) {
int[] intArray = s[i].toArray(ValueLayout.JAVA_INT);
System.out.println(Arrays.toString(intArray));
}
} catch (Exception e) {
e.printStackTrace();
}
}
メモリー・セグメントのスライス
スライス・アロケータがスライスを返す場合、スライスの開始アドレスは、スライス・アロケータが戻した最後のスライスの終了直後になります。MemorySegment.asSlice(long,long)を呼び出して、メモリー・セグメント内の任意の場所の任意のサイズのメモリー・セグメントのスライスを取得できます(スライスのサイズが元のメモリー・セグメントの空間的境界内に収まっていることが条件)。次の例では、メモリー・セグメントのスライスを取得し、その内容を出力します。
String s = "abcdefghijklmnopqrstuvwxyz";
char c[] = s.toCharArray();
MemorySegment textSegment = MemorySegment.ofArray(c);
long b = ValueLayout.JAVA_CHAR.byteSize();
long firstLetter = 5;
long size = 6;
MemorySegment fghijk = textSegment.asSlice(firstLetter*b, size*b);
for (int i = 0; i < size; i++) {
System.out.print((char)fghijk.get(ValueLayout.JAVA_CHAR, i*b));
}
System.out.println();
この例の出力は次のとおりです。
fghijk
メソッドMemorySegment.elements(MemoryLayout)は、指定されたレイアウトのサイズと一致するスライスのストリームを返します。複数のスレッドが並行して機能し、これらのスライスにアクセスできます。ただし、これを行うには、複数のスレッドからメモリー・セグメントにアクセスできる必要があります。これを行うには、メモリー・セグメントを共有アリーナに関連付けます。これは、Arena::ofShared
で作成できます。
次の例では、メモリー・セグメント内のすべてのint
値をパラレルに合計します。
void addRandomNumbers(int numElements) throws Throwable {
int[] numbers = new Random().ints(numElements, 0, 1000).toArray();
try (Arena arena = Arena.ofShared()) {
SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout((long)numElements, ValueLayout.JAVA_INT);
MemorySegment segment = arena.allocate(SEQUENCE_LAYOUT);
MemorySegment.copy(numbers, 0, segment, ValueLayout.JAVA_INT, 0L, numElements);
int sum = segment.elements(ValueLayout.JAVA_INT).parallel()
.mapToInt(s -> s.get(ValueLayout.JAVA_INT, 0))
.sum();
System.out.println(sum);
}
}