Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add first stab at a MemoryManager for Muninn
The idea is to be able to allocate the cache pages at any alignment that the PageSwappers might require. Useful if we want to start doing direct-io.
- Loading branch information
Showing
7 changed files
with
214 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 134 additions & 0 deletions
134
community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MemoryManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
* Copyright (c) 2002-2015 "Neo Technology," | ||
* Network Engine for Objects in Lund AB [http://neotechnology.com] | ||
* | ||
* This file is part of Neo4j. | ||
* | ||
* Neo4j is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package org.neo4j.io.pagecache.impl.muninn; | ||
|
||
import org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil; | ||
|
||
/** | ||
* The memory manager is simple: it only allocates memory, until it itself is finalizable and frees it all in one go. | ||
* | ||
* The memory is allocated in large segments, and the memory returned by the memory manager is page aligned, and plays | ||
* well with transparent huge pages and other operating system optimisations. | ||
* | ||
* The memory manager assumes that the memory claimed from it is evenly divisible in units of pages. | ||
*/ | ||
final class MemoryManager | ||
{ | ||
private static final long GRAB_SIZE = 32 * 1024 * 1024; // 32 MiB | ||
|
||
/** | ||
* The amount of memory that this memory manager can still allocate. | ||
*/ | ||
private long memoryReserve; | ||
private final long alignment; | ||
|
||
private Slab slabs; | ||
|
||
/** | ||
* Create a new MemoryManager that will allocate the given amount of memory, to pointers that are aligned to the | ||
* given alignment size. | ||
* @param expectedMaxMemory The maximum amount of memory that this memory manager is expected to allocate. The | ||
* actual amount of memory used can end up greater than this value, if some of it gets wasted on alignment padding. | ||
* @param alignment The byte multiple that the allocated pointers have to be aligned at. | ||
*/ | ||
public MemoryManager( long expectedMaxMemory, long alignment ) | ||
{ | ||
this.memoryReserve = expectedMaxMemory; | ||
this.alignment = alignment; | ||
} | ||
|
||
/** | ||
* Allocate a contiguous, aligned region of memory of the given size in bytes. | ||
* @param bytes the number of bytes to allocate. | ||
* @return A pointer to the allocated memory. | ||
*/ | ||
public synchronized long allocateAligned( long bytes ) | ||
{ | ||
if ( slabs == null || !slabs.canAllocate( bytes ) ) | ||
{ | ||
long slabGrab = Math.min( GRAB_SIZE, memoryReserve ); | ||
if ( slabGrab < bytes ) | ||
{ | ||
slabGrab = bytes + alignment; | ||
} | ||
memoryReserve -= slabGrab; | ||
slabs = new Slab( slabs, slabGrab, alignment ); | ||
} | ||
return slabs.allocate( bytes ); | ||
} | ||
|
||
@Override | ||
protected synchronized void finalize() throws Throwable | ||
{ | ||
super.finalize(); | ||
Slab current = slabs; | ||
|
||
while ( current != null ) | ||
{ | ||
current.free(); | ||
current = current.next; | ||
} | ||
} | ||
|
||
private static class Slab | ||
{ | ||
public final Slab next; | ||
private final long address; | ||
private final long limit; | ||
private final long alignMask; | ||
private long nextAlignedPointer; | ||
|
||
public Slab( Slab next, long size, long alignment ) | ||
{ | ||
this.next = next; | ||
this.address = UnsafeUtil.malloc( size ); | ||
this.limit = address + size; | ||
this.alignMask = alignment - 1; | ||
|
||
nextAlignedPointer = nextAligned( address ); | ||
} | ||
|
||
private long nextAligned( long pointer ) | ||
{ | ||
if ( (pointer & ~alignMask) == pointer ) | ||
{ | ||
return pointer; | ||
} | ||
return (pointer + alignMask) & ~alignMask; | ||
} | ||
|
||
public long allocate( long bytes ) | ||
{ | ||
long allocation = nextAlignedPointer; | ||
nextAlignedPointer = nextAligned( nextAlignedPointer + bytes ); | ||
return allocation; | ||
} | ||
|
||
public void free() | ||
{ | ||
UnsafeUtil.free( address ); | ||
} | ||
|
||
public boolean canAllocate( long bytes ) | ||
{ | ||
return nextAlignedPointer + bytes <= limit; | ||
} | ||
} | ||
} |
53 changes: 0 additions & 53 deletions
53
community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MemoryReleaser.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/MemoryManagerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Copyright (c) 2002-2015 "Neo Technology," | ||
* Network Engine for Objects in Lund AB [http://neotechnology.com] | ||
* | ||
* This file is part of Neo4j. | ||
* | ||
* Neo4j is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package org.neo4j.io.pagecache.impl.muninn; | ||
|
||
import org.junit.Test; | ||
|
||
import org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil; | ||
|
||
import static org.hamcrest.Matchers.is; | ||
import static org.hamcrest.Matchers.not; | ||
import static org.junit.Assert.*; | ||
|
||
public class MemoryManagerTest | ||
{ | ||
@Test | ||
public void allocatedPointerMustNotBeNull() throws Exception | ||
{ | ||
MemoryManager mman = new MemoryManager( 16 * 4096, 8 ); | ||
long address = mman.allocateAligned( 8192 ); | ||
assertThat( address, is( not( 0L ) ) ); | ||
} | ||
|
||
@Test | ||
public void allocatedPointerMustBePageAligned() throws Exception | ||
{ | ||
MemoryManager mman = new MemoryManager( 16 * 4096, UnsafeUtil.pageSize() ); | ||
long address = mman.allocateAligned( 8192 ); | ||
assertThat( address % UnsafeUtil.pageSize(), is( 0L ) ); | ||
} | ||
|
||
@Test | ||
public void mustBeAbleToAllocatePastMemoryLimit() throws Exception | ||
{ | ||
MemoryManager mman = new MemoryManager( 8192, 2 ); | ||
for ( int i = 0; i < 4100; i++ ) | ||
{ | ||
assertThat( mman.allocateAligned( 1 ) % 2, is( 0L ) ); | ||
} | ||
// Also asserts that no OutOfMemoryError is thrown. | ||
} | ||
} |