Skip to content

Commit

Permalink
Increase the swapper id space from 16 bit to 21 bit.
Browse files Browse the repository at this point in the history
This means that the page cache can now have roughly 2 million files mapped, instead of the 32.768 it could handle before.
  • Loading branch information
chrisvest committed Apr 27, 2018
1 parent f6f0f97 commit 3340c3e
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 54 deletions.
Expand Up @@ -56,8 +56,6 @@ class PageList
public static final long MAX_PAGES = Integer.MAX_VALUE;

private static final int UNBOUND_LAST_MODIFIED_TX_ID = -1;
private static final int UNSIGNED_BYTE_MASK = 0xFF;
private static final long UNSIGNED_INT_MASK = 0xFFFFFFFFL;

// 40 bits for file page id
private static final long MAX_FILE_PAGE_ID = 0b11111111_11111111_11111111_11111111_11111111L;
Expand All @@ -66,8 +64,10 @@ class PageList
private static final int OFFSET_ADDRESS = 8; // 8 bytes
private static final int OFFSET_LAST_TX_ID = 16; // 8 bytes
private static final int OFFSET_FILE_PAGE_ID = 24; // 5 bytes
private static final int OFFSET_USAGE_COUNTER = 29; // 1 byte
private static final int OFFSET_SWAPPER_ID = 30; // 2 bytes
@SuppressWarnings( "unused" )
private static final int OFFSET_SWAPPER_ID = 29; // 2 bytes, plus the 5 high-bits from usage counter
@SuppressWarnings( "unused" )
private static final int OFFSET_USAGE_COUNTER = 31; // 1 byte, but only the 3 low bits.

// todo we can alternatively also make use of the lower 12 bits of the address field, because
// todo the addresses are page aligned, and we can assume them to be at least 4096 bytes in size.
Expand Down Expand Up @@ -178,7 +178,7 @@ private void clearMemorySimple( long baseAddress, long pageCount )
UnsafeUtil.putLong( address += Long.BYTES, initialLockWord ); // lock word
UnsafeUtil.putLong( address += Long.BYTES, 0 ); // pointer
UnsafeUtil.putLong( address += Long.BYTES, 0 ); // last tx id
UnsafeUtil.putLong( address += Long.BYTES, MAX_FILE_PAGE_ID );
UnsafeUtil.putLong( address += Long.BYTES, MAX_FILE_PAGE_ID << 24 );
}
}

Expand Down Expand Up @@ -247,21 +247,11 @@ private long offAddress( long pageRef )
return pageRef + OFFSET_ADDRESS;
}

private long offUsage( long pageRef )
{
return pageRef + OFFSET_USAGE_COUNTER;
}

private long offFilePageId( long pageRef )
{
return pageRef + OFFSET_FILE_PAGE_ID;
}

private long offSwapperId( long pageRef )
{
return pageRef + OFFSET_SWAPPER_ID;
}

public long tryOptimisticReadLock( long pageRef )
{
return OffHeapPageLock.tryOptimisticReadLock( offLock( pageRef ) );
Expand Down Expand Up @@ -348,12 +338,7 @@ public void initBuffer( long pageRef )

private byte getUsageCounter( long pageRef )
{
return UnsafeUtil.getByteVolatile( offUsage( pageRef ) );
}

private void setUsageCounter( long pageRef, byte count )
{
UnsafeUtil.putByteVolatile( offUsage( pageRef ), count );
return (byte) (UnsafeUtil.getLongVolatile( offFilePageId( pageRef ) ) & 0x07);
}

/**
Expand All @@ -362,11 +347,18 @@ private void setUsageCounter( long pageRef, byte count )
public void incrementUsage( long pageRef )
{
// This is intentionally left benignly racy for performance.
byte usage = getUsageCounter( pageRef );
long address = offFilePageId( pageRef );
long v = UnsafeUtil.getLongVolatile( address );
long usage = v & 0x07;
if ( usage < 4 ) // avoid cache sloshing by not doing a write if counter is already maxed out
{
usage++;
setUsageCounter( pageRef, usage );
// Use compareAndSwapLong to only actually store the updated count if nothing else changed
// in this word-line. The word-line is shared with the file page id, and the swapper id.
// Those fields are updated under guard of the exclusive lock, but we *might* race with
// that here, and in that case we would never want a usage counter update to clobber a page
// binding update.
UnsafeUtil.compareAndSwapLong( null, address, v, (v & 0xFFFFFFFF_FFFFFFF8L) + usage );
}
}

Expand All @@ -376,20 +368,21 @@ public void incrementUsage( long pageRef )
public boolean decrementUsage( long pageRef )
{
// This is intentionally left benignly racy for performance.
byte usage = getUsageCounter( pageRef );
long address = offFilePageId( pageRef );
long v = UnsafeUtil.getLongVolatile( address );
long usage = v & 0x07;
if ( usage > 0 )
{
usage--;
setUsageCounter( pageRef, usage );
// See `incrementUsage` about why we use `compareAndSwapLong`.
UnsafeUtil.compareAndSwapLong( null, address, v, (v & 0xFFFFFFFF_FFFFFFF8L) + usage );
}
return usage == 0;
}

public long getFilePageId( long pageRef )
{
int highByte = UnsafeUtil.getByte( offFilePageId( pageRef ) ) & UNSIGNED_BYTE_MASK;
long lowInt = UnsafeUtil.getInt( offFilePageId( pageRef ) + Byte.BYTES ) & UNSIGNED_INT_MASK;
long filePageId = (((long) highByte) << Integer.SIZE) | lowInt;
long filePageId = UnsafeUtil.getLong( offFilePageId( pageRef ) ) >>> 24;
return filePageId == MAX_FILE_PAGE_ID ? PageCursor.UNBOUND_PAGE_ID : filePageId;
}

Expand All @@ -400,9 +393,10 @@ private void setFilePageId( long pageRef, long filePageId )
throw new IllegalArgumentException(
format( "File page id: %s is bigger then max supported value %s.", filePageId, MAX_FILE_PAGE_ID ) );
}
byte highByte = (byte) (filePageId >> Integer.SIZE);
UnsafeUtil.putByte( offFilePageId( pageRef ), highByte );
UnsafeUtil.putInt( offFilePageId( pageRef ) + Byte.BYTES, (int) filePageId );
long address = offFilePageId( pageRef );
long v = UnsafeUtil.getLong( address );
filePageId = (filePageId << 24) + (v & 0xFFFFFF);
UnsafeUtil.putLong( address, filePageId );
}

long getLastModifiedTxId( long pageRef )
Expand All @@ -423,27 +417,31 @@ void setLastModifiedTxId( long pageRef, long modifierTxId )
UnsafeUtil.compareAndSetMaxLong( null, offLastModifiedTransactionId( pageRef ), modifierTxId );
}

public short getSwapperId( long pageRef )
public int getSwapperId( long pageRef )
{
return UnsafeUtil.getShort( offSwapperId( pageRef ) );
long v = UnsafeUtil.getLong( offFilePageId( pageRef ) ) >>> 3;
return (int) (v & 0b1_11111_11111_11111_11111); // 21 bits.
}

private void setSwapperId( long pageRef, short swapperId )
private void setSwapperId( long pageRef, int swapperId )
{
UnsafeUtil.putShort( offSwapperId( pageRef ), swapperId );
swapperId = swapperId << 3;
long address = offFilePageId( pageRef );
long v = UnsafeUtil.getLong( address ) & (~(0b1_11111_11111_11111_11111 << 3));
UnsafeUtil.putLong( address, v + swapperId );
}

public boolean isLoaded( long pageRef )
{
return getFilePageId( pageRef ) != PageCursor.UNBOUND_PAGE_ID;
}

public boolean isBoundTo( long pageRef, short swapperId, long filePageId )
public boolean isBoundTo( long pageRef, int swapperId, long filePageId )
{
return getSwapperId( pageRef ) == swapperId && getFilePageId( pageRef ) == filePageId;
}

public void fault( long pageRef, PageSwapper swapper, short swapperId, long filePageId, PageFaultEvent event )
public void fault( long pageRef, PageSwapper swapper, int swapperId, long filePageId, PageFaultEvent event )
throws IOException
{
if ( swapper == null )
Expand Down Expand Up @@ -476,7 +474,7 @@ private static IllegalArgumentException swapperCannotBeNull()
return new IllegalArgumentException( "swapper cannot be null" );
}

private static IllegalStateException cannotFaultException( long pageRef, PageSwapper swapper, short swapperId,
private static IllegalStateException cannotFaultException( long pageRef, PageSwapper swapper, int swapperId,
long filePageId, int currentSwapper, long currentFilePageId )
{
String msg = format(
Expand Down Expand Up @@ -509,7 +507,7 @@ private void evict( long pageRef, EvictionEvent evictionEvent ) throws IOExcepti
long filePageId = getFilePageId( pageRef );
evictionEvent.setFilePageId( filePageId );
evictionEvent.setCachePageId( pageRef );
short swapperId = getSwapperId( pageRef );
int swapperId = getSwapperId( pageRef );
if ( swapperId != 0 )
{
// If the swapper id is non-zero, then the page was not only loaded, but also bound, and possibly modified.
Expand Down
Expand Up @@ -45,7 +45,7 @@ final class SwapperSet
// The tombstone is used as a marker to reserve allocation entries that have been freed, but not yet vacuumed.
// An allocation cannot be reused until it has been vacuumed.
private static final SwapperMapping TOMBSTONE = new SwapperMapping( 0, null );
private static final int MAX_SWAPPER_ID = Short.MAX_VALUE;
private static final int MAX_SWAPPER_ID = (1 << 21) - 1;
private volatile SwapperMapping[] swapperMappings = new SwapperMapping[] { SENTINEL };
private final PrimitiveIntSet free = Primitive.intSet();
private final Object vacuumLock = new Object();
Expand All @@ -69,7 +69,7 @@ private SwapperMapping( int id, PageSwapper swapper )
/**
* Get the {@link SwapperMapping} for the given swapper id.
*/
SwapperMapping getAllocation( short id )
SwapperMapping getAllocation( int id )
{
checkId( id );
SwapperMapping swapperMapping = swapperMappings[id];
Expand Down Expand Up @@ -197,10 +197,10 @@ void vacuum( Consumer<IntPredicate> evictAllLoadedPagesCallback )
}
}

synchronized short countAvailableIds()
synchronized int countAvailableIds()
{
// the max id is one less than the allowed count, but we subtract one for the reserved id 0
short available = MAX_SWAPPER_ID;
int available = MAX_SWAPPER_ID;
available -= swapperMappings.length; // ids that are allocated are not available
available += free.size(); // add back the ids that are free to be reused
return available;
Expand Down
Expand Up @@ -1369,7 +1369,7 @@ public long read( long fpId, long bufferAddress, int bufferSize ) throws IOExcep
public void pageMustBeLoadedAndBoundAfterFault() throws Exception
{
// exclusive lock implied by constructor
short swapperId = 1;
int swapperId = 1;
long filePageId = 42;
pageList.initBuffer( pageRef );
pageList.fault( pageRef, DUMMY_SWAPPER, swapperId, filePageId, PageFaultEvent.NULL );
Expand All @@ -1383,7 +1383,7 @@ public void pageMustBeLoadedAndBoundAfterFault() throws Exception
public void pageWith5BytesFilePageIdMustBeLoadedAndBoundAfterFault() throws Exception
{
// exclusive lock implied by constructor
short swapperId = 12;
int swapperId = 12;
long filePageId = Integer.MAX_VALUE + 1L;
pageList.initBuffer( pageRef );
pageList.fault( pageRef, DUMMY_SWAPPER, swapperId, filePageId, PageFaultEvent.NULL );
Expand All @@ -1405,7 +1405,7 @@ public long read( long filePageId, long bufferAddress, int bufferSize ) throws I
throw new IOException( "boo" );
}
};
short swapperId = 1;
int swapperId = 1;
long filePageId = 42;
pageList.initBuffer( pageRef );
try
Expand All @@ -1418,7 +1418,7 @@ public long read( long filePageId, long bufferAddress, int bufferSize ) throws I
assertThat( e.getMessage(), is( "boo" ) );
}
assertThat( pageList.getFilePageId( pageRef ), is( filePageId ) );
assertThat( pageList.getSwapperId( pageRef ), is( (short) 0 ) ); // 0 means not bound
assertThat( pageList.getSwapperId( pageRef ), is( 0 ) ); // 0 means not bound
assertTrue( pageList.isLoaded( pageRef ) );
assertFalse( pageList.isBoundTo( pageRef, swapperId, filePageId ) );
}
Expand Down Expand Up @@ -1631,16 +1631,16 @@ public void pageMustNotBeLoadedAfterSuccessfulEviction() throws Exception
public void pageMustNotBeBoundAfterSuccessfulEviction() throws Exception
{
pageList.unlockExclusive( pageRef );
short swapperId = swappers.allocate( DUMMY_SWAPPER );
int swapperId = swappers.allocate( DUMMY_SWAPPER );
doFault( swapperId, 42 ); // page now bound & exclusively locked
pageList.unlockExclusive( pageRef ); // no longer exclusively locked; can now be evicted
assertTrue( pageList.isBoundTo( pageRef, (short) 1, 42 ) );
assertTrue( pageList.isLoaded( pageRef ) );
assertThat( pageList.getSwapperId( pageRef ), is( (short) 1 ) );
assertThat( pageList.getSwapperId( pageRef ), is( 1 ) );
pageList.tryEvict( pageRef, EvictionRunEvent.NULL );
assertFalse( pageList.isBoundTo( pageRef, (short) 1, 42 ) );
assertFalse( pageList.isLoaded( pageRef ) );
assertThat( pageList.getSwapperId( pageRef ), is( (short) 0 ) );
assertThat( pageList.getSwapperId( pageRef ), is( 0 ) );
}

@Test
Expand Down Expand Up @@ -2071,7 +2071,7 @@ public void failToSetHigherThenSupportedFilePageIdOnFault() throws IOException
doFault( swapperId, Long.MAX_VALUE );
}

private void doFault( short swapperId, long filePageId ) throws IOException
private void doFault( int swapperId, long filePageId ) throws IOException
{
assertTrue( pageList.tryExclusiveLock( pageRef ) );
pageList.initBuffer( pageRef );
Expand Down
Expand Up @@ -188,12 +188,12 @@ public void freeOfIdZeroMustThrow()
public void mustKeepTrackOfAvailableSwapperIds()
{
PageSwapper swapper = new DummyPageSwapper( "a", 42 );
short initial = Short.MAX_VALUE - 1;
int initial = (1 << 21) - 2;
assertThat( set.countAvailableIds(), is( initial ) );
int id = set.allocate( swapper );
assertThat( set.countAvailableIds(), is( (short) (initial - 1) ) );
assertThat( set.countAvailableIds(), is( initial - 1 ) );
set.free( id );
assertThat( set.countAvailableIds(), is( (short) (initial - 1) ) );
assertThat( set.countAvailableIds(), is( initial - 1 ) );
set.vacuum( x -> {} );
assertThat( set.countAvailableIds(), is( initial ) );
}
Expand Down

0 comments on commit 3340c3e

Please sign in to comment.