This is an automated email from the ASF dual-hosted git repository.
ibessonov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 81ea8860178 IGNITE-27591 Replace shared acquirePages memory with
LongAdder (#7425)
81ea8860178 is described below
commit 81ea8860178de03f02219d801a53d17db1b55148
Author: Ivan Bessonov <[email protected]>
AuthorDate: Mon Mar 23 10:40:09 2026 +0300
IGNITE-27591 Replace shared acquirePages memory with LongAdder (#7425)
---
.../apache/ignite/internal/util/GridUnsafe.java | 4 +
.../pagememory/persistence/PageHeader.java | 6 +-
.../persistence/PersistentPageMemory.java | 113 +++++++--------------
.../pagememory/persistence/PageHeaderTest.java | 2 +-
.../pagememory/PersistentPageMemoryDataRegion.java | 2 +-
5 files changed, 47 insertions(+), 80 deletions(-)
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java
b/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java
index f1d6cb6aa3a..b0b85717665 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java
@@ -1285,6 +1285,10 @@ public abstract class GridUnsafe {
return UNSAFE.getAndAddInt(null, ptr, 1) + 1;
}
+ public static int getAndIncrementInt(long ptr) {
+ return UNSAFE.getAndAddInt(null, ptr, 1);
+ }
+
/**
* Atomically increments value stored in an integer pointed by {@code ptr}.
*
diff --git
a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/persistence/PageHeader.java
b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/persistence/PageHeader.java
index 03ec4bca0ab..36ba0a6996f 100644
---
a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/persistence/PageHeader.java
+++
b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/persistence/PageHeader.java
@@ -22,13 +22,13 @@ import static
org.apache.ignite.internal.util.GridUnsafe.decrementAndGetInt;
import static org.apache.ignite.internal.util.GridUnsafe.getInt;
import static org.apache.ignite.internal.util.GridUnsafe.getIntVolatile;
import static org.apache.ignite.internal.util.GridUnsafe.getLong;
-import static org.apache.ignite.internal.util.GridUnsafe.incrementAndGetInt;
import static org.apache.ignite.internal.util.GridUnsafe.putInt;
import static org.apache.ignite.internal.util.GridUnsafe.putIntVolatile;
import static org.apache.ignite.internal.util.GridUnsafe.putLong;
import static org.apache.ignite.internal.util.GridUnsafe.putLongVolatile;
import org.apache.ignite.internal.pagememory.FullPageId;
+import org.apache.ignite.internal.util.GridUnsafe;
/**
* Helper class for working with the page header that is stored in memory for
{@link PersistentPageMemory}.
@@ -198,10 +198,10 @@ public class PageHeader {
* Acquires a page.
*
* @param absPtr Absolute pointer.
- * @return Number of acquires for the page.
+ * @return Previous number of acquires for the page.
*/
public static int acquirePage(long absPtr) {
- return incrementAndGetInt(absPtr + PAGE_PIN_CNT_OFFSET);
+ return GridUnsafe.getAndIncrementInt(absPtr + PAGE_PIN_CNT_OFFSET);
}
/**
diff --git
a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/persistence/PersistentPageMemory.java
b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/persistence/PersistentPageMemory.java
index 23176422d06..fc1df828f97 100644
---
a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/persistence/PersistentPageMemory.java
+++
b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/persistence/PersistentPageMemory.java
@@ -50,11 +50,7 @@ import static
org.apache.ignite.internal.util.FastTimestamps.coarseCurrentTimeMi
import static org.apache.ignite.internal.util.GridUnsafe.BYTE_ARR_OFF;
import static org.apache.ignite.internal.util.GridUnsafe.bufferAddress;
import static org.apache.ignite.internal.util.GridUnsafe.copyMemory;
-import static org.apache.ignite.internal.util.GridUnsafe.decrementAndGetInt;
-import static org.apache.ignite.internal.util.GridUnsafe.getInt;
import static org.apache.ignite.internal.util.GridUnsafe.getLong;
-import static org.apache.ignite.internal.util.GridUnsafe.incrementAndGetInt;
-import static org.apache.ignite.internal.util.GridUnsafe.putIntVolatile;
import static org.apache.ignite.internal.util.GridUnsafe.wrapPointer;
import static org.apache.ignite.internal.util.GridUnsafe.zeroMemory;
import static org.apache.ignite.internal.util.IgniteUtils.hash;
@@ -77,6 +73,7 @@ import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@@ -1029,28 +1026,6 @@ public class PersistentPageMemory implements PageMemory {
return total;
}
- /**
- * Returns {@code true} if the page is contained in the loaded pages
table, {@code false} otherwise.
- *
- * @param fullPageId Full page ID to check.
- */
- public boolean hasLoadedPage(FullPageId fullPageId) {
- int grpId = fullPageId.groupId();
- long pageId = fullPageId.effectivePageId();
-
- Segment seg = segment(grpId, pageId);
-
- seg.readLock().lock();
-
- try {
- long res = seg.loadedPages.get(grpId, pageId, partGeneration(seg,
fullPageId), INVALID_REL_PTR, INVALID_REL_PTR);
-
- return res != INVALID_REL_PTR;
- } finally {
- seg.readLock().unlock();
- }
- }
-
/** {@inheritDoc} */
@Override
public long readLockForce(int grpId, long pageId, long page) {
@@ -1107,10 +1082,12 @@ public class PersistentPageMemory implements PageMemory
{
private long postWriteLockPage(long absPtr, FullPageId fullId) {
timestamp(absPtr, coarseCurrentTimeMillis());
+ Segment seg = segment(fullId.groupId(), fullId.pageId());
+
DirtyFullPageId dirtyFullId = dirtyFullPageId(absPtr);
// Create a buffer copy if the page is scheduled for a checkpoint.
- if (isInCheckpoint(dirtyFullId) && tempBufferPointer(absPtr) ==
INVALID_REL_PTR) {
+ if (isInCheckpoint(seg, dirtyFullId) && tempBufferPointer(absPtr) ==
INVALID_REL_PTR) {
long tmpRelPtr;
while (true) {
@@ -1129,7 +1106,7 @@ public class PersistentPageMemory implements PageMemory {
}
// Pin the page until checkpoint is not finished.
- PageHeader.acquirePage(absPtr);
+ seg.acquirePage(absPtr);
long tmpAbsPtr = checkpointPool.absolute(tmpRelPtr);
@@ -1228,19 +1205,6 @@ public class PersistentPageMemory implements PageMemory {
return rwLock.isReadLocked(absPtr + PAGE_LOCK_OFFSET);
}
- /**
- * Returns the number of active pages across all segments. Used for test
purposes only.
- */
- public int activePagesCount() {
- int total = 0;
-
- for (Segment seg : segments) {
- total += seg.acquiredPages();
- }
-
- return total;
- }
-
/**
* This method must be called in synchronized context.
*
@@ -1345,17 +1309,11 @@ public class PersistentPageMemory implements PageMemory
{
/** Serial version uid. */
private static final long serialVersionUID = 0L;
- /** Pointer to acquired pages integer counter. */
- private static final int ACQUIRED_PAGES_SIZEOF = 4;
-
- /** Padding to read from word beginning. */
- private static final int ACQUIRED_PAGES_PADDING = 4;
-
/** Page ID to relative pointer map. */
private final LoadedPagesMap loadedPages;
- /** Pointer to acquired pages integer counter. */
- private final long acquiredPagesPtr;
+ /** Acquired pages counter. */
+ private final LongAdder acquiredPages = new LongAdder();
/** Page pool. */
private final PagePool pool;
@@ -1409,29 +1367,23 @@ public class PersistentPageMemory implements PageMemory
{
int pages = (int) (totalMemory / sysPageSize);
- acquiredPagesPtr = region.address();
-
- putIntVolatile(null, acquiredPagesPtr, 0);
-
- int ldPagesMapOffInRegion = ACQUIRED_PAGES_SIZEOF +
ACQUIRED_PAGES_PADDING;
-
- long ldPagesAddr = region.address() + ldPagesMapOffInRegion;
+ long ldPagesAddr = region.address();
memPerTbl = RobinHoodBackwardShiftHashMap.requiredMemory(pages);
loadedPages = new RobinHoodBackwardShiftHashMap(ldPagesAddr,
memPerTbl);
- pages = (int) ((totalMemory - memPerTbl - ldPagesMapOffInRegion) /
sysPageSize);
+ pages = (int) ((totalMemory - memPerTbl) / sysPageSize);
memPerRepl = pageReplacementPolicyFactory.requiredMemory(pages);
- DirectMemoryRegion poolRegion = region.slice(memPerTbl +
memPerRepl + ldPagesMapOffInRegion);
+ DirectMemoryRegion poolRegion = region.slice(memPerTbl +
memPerRepl);
pool = new PagePool(idx, poolRegion, sysPageSize, rwLock);
pageReplacementPolicy = pageReplacementPolicyFactory.create(
this,
- region.address() + memPerTbl + ldPagesMapOffInRegion,
+ region.address() + memPerTbl,
pool.pages()
);
@@ -1478,23 +1430,27 @@ public class PersistentPageMemory implements PageMemory
{
return memPerRepl;
}
- private void acquirePage(long absPtr) {
- PageHeader.acquirePage(absPtr);
+ protected void acquirePage(long absPtr) {
+ int oldPinCount = PageHeader.acquirePage(absPtr);
- incrementAndGetInt(acquiredPagesPtr);
+ if (oldPinCount == 0) {
+ acquiredPages.increment();
+ }
}
- private void releasePage(long absPtr) {
- PageHeader.releasePage(absPtr);
+ protected void releasePage(long absPtr) {
+ int newPinCount = PageHeader.releasePage(absPtr);
- decrementAndGetInt(acquiredPagesPtr);
+ if (newPinCount == 0) {
+ acquiredPages.decrement();
+ }
}
/**
* Returns total number of acquired pages.
*/
private int acquiredPages() {
- return getInt(acquiredPagesPtr);
+ return acquiredPages.intValue();
}
/**
@@ -1616,7 +1572,7 @@ public class PersistentPageMemory implements PageMemory {
tempBufferPointer(absPtr, INVALID_REL_PTR);
// We pinned the page when allocated the temp buffer, release
it now.
- PageHeader.releasePage(absPtr);
+ releasePage(absPtr);
releaseCheckpointBufferPage(tmpBufPtr);
}
@@ -1854,6 +1810,10 @@ public class PersistentPageMemory implements PageMemory {
private boolean isInCheckpoint(DirtyFullPageId pageId) {
Segment seg = segment(pageId.groupId(), pageId.pageId());
+ return isInCheckpoint(seg, pageId);
+ }
+
+ private static boolean isInCheckpoint(Segment seg, DirtyFullPageId pageId)
{
CheckpointPages pages0 = seg.checkpointPages;
return pages0 != null && pages0.contains(pageId);
@@ -1877,6 +1837,7 @@ public class PersistentPageMemory implements PageMemory {
/**
* Makes a full copy of the dirty page for checkpointing, then marks the
page as not dirty.
*
+ * @param seg Segment.
* @param absPtr Absolute page pointer.
* @param fullId Full page ID.
* @param buf Buffer for copy page content for future write via {@link
PageStoreWriter}.
@@ -1889,6 +1850,7 @@ public class PersistentPageMemory implements PageMemory {
* <b>write lock</b> on page.
*/
private void copyPageForCheckpoint(
+ Segment seg,
long absPtr,
DirtyFullPageId fullId,
ByteBuffer buf,
@@ -1899,19 +1861,19 @@ public class PersistentPageMemory implements PageMemory
{
boolean useTryWriteLockOnPage
) throws IgniteInternalCheckedException {
assert absPtr != 0 : fullId.pageId();
- assert isAcquired(absPtr) || !isInCheckpoint(fullId) : fullId.pageId();
+ assert isAcquired(absPtr) || !isInCheckpoint(seg, fullId) :
fullId.pageId();
if (useTryWriteLockOnPage) {
if (!rwLock.tryWriteLock(absPtr + PAGE_LOCK_OFFSET,
TAG_LOCK_ALWAYS)) {
// We release the page only once here because this page will
be copied sometime later and
// will be released properly then.
if (!pageSingleAcquire) {
- PageHeader.releasePage(absPtr);
+ seg.releasePage(absPtr);
}
buf.clear();
- if (isInCheckpoint(fullId)) {
+ if (isInCheckpoint(seg, fullId)) {
pageStoreWriter.writePage(fullId, buf, TRY_AGAIN_TAG);
}
@@ -1927,7 +1889,7 @@ public class PersistentPageMemory implements PageMemory {
rwLock.writeUnlock(absPtr + PAGE_LOCK_OFFSET, TAG_LOCK_ALWAYS);
if (!pageSingleAcquire) {
- PageHeader.releasePage(absPtr);
+ seg.releasePage(absPtr);
}
return;
@@ -1958,7 +1920,7 @@ public class PersistentPageMemory implements PageMemory {
// Need release again because we pin page when resolve abs
pointer,
// and page did not have tmp buffer page.
if (!pageSingleAcquire) {
- PageHeader.releasePage(absPtr);
+ seg.releasePage(absPtr);
}
} else {
copyInBuffer(absPtr, buf);
@@ -1985,7 +1947,7 @@ public class PersistentPageMemory implements PageMemory {
// We pinned the page either when allocated the temp buffer, or
when resolved abs pointer.
// Must release the page only after write unlock.
- PageHeader.releasePage(absPtr);
+ seg.releasePage(absPtr);
}
}
@@ -2024,7 +1986,7 @@ public class PersistentPageMemory implements PageMemory {
seg.readLock().lock();
try {
- if (!isInCheckpoint(fullId)) {
+ if (!isInCheckpoint(seg, fullId)) {
return;
}
@@ -2046,7 +2008,7 @@ public class PersistentPageMemory implements PageMemory {
// Pin the page until page will not be copied. This helpful to
prevent page replacement of this page.
if (tempBufferPointer(absPtr) == INVALID_REL_PTR) {
- PageHeader.acquirePage(absPtr);
+ seg.acquirePage(absPtr);
} else {
pageSingleAcquire = true;
}
@@ -2085,6 +2047,7 @@ public class PersistentPageMemory implements PageMemory {
}
copyPageForCheckpoint(
+ seg,
absPtr,
fullId,
buf,
diff --git
a/modules/page-memory/src/test/java/org/apache/ignite/internal/pagememory/persistence/PageHeaderTest.java
b/modules/page-memory/src/test/java/org/apache/ignite/internal/pagememory/persistence/PageHeaderTest.java
index ccc95286b06..1819ef3b094 100644
---
a/modules/page-memory/src/test/java/org/apache/ignite/internal/pagememory/persistence/PageHeaderTest.java
+++
b/modules/page-memory/src/test/java/org/apache/ignite/internal/pagememory/persistence/PageHeaderTest.java
@@ -117,7 +117,7 @@ public class PageHeaderTest {
@Test
void testAcquireRelease() {
- assertEquals(1, acquirePage(pageHeaderAddr));
+ assertEquals(0, acquirePage(pageHeaderAddr));
assertEquals(1, pinCount(pageHeaderAddr));
assertTrue(isAcquired(pageHeaderAddr));
diff --git
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryDataRegion.java
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryDataRegion.java
index acb2dd982c2..d859e3d465c 100644
---
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryDataRegion.java
+++
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryDataRegion.java
@@ -182,7 +182,7 @@ public class PersistentPageMemoryDataRegion implements
DataRegion<PersistentPage
regionConfiguration(dataRegionConfigView, sizeBytes, pageSize),
metricSource,
ioRegistry,
- calculateSegmentSizes(sizeBytes,
Runtime.getRuntime().availableProcessors()),
+ calculateSegmentSizes(sizeBytes,
Runtime.getRuntime().availableProcessors() * 4),
calculateCheckpointBufferSize(sizeBytes),
filePageStoreManager,
this::flushDirtyPageOnReplacement,