This is an automated email from the ASF dual-hosted git repository.
ivandasch pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 3c27c15 IGNITE-15922 Implement NUMA-aware allocator - Fixes #9569.
3c27c15 is described below
commit 3c27c1579a11727e425e7132def72c859ec4de24
Author: Ivan Daschinsky <[email protected]>
AuthorDate: Mon Dec 13 16:09:50 2021 +0300
IGNITE-15922 Implement NUMA-aware allocator - Fixes #9569.
Signed-off-by: Ivan Daschinsky <[email protected]>
---
.travis.yml | 15 +-
.../configuration/DataRegionConfiguration.java | 23 +++
.../configuration/DataStorageConfiguration.java | 25 +++
.../internal/mem/unsafe/UnsafeMemoryAllocator.java | 33 ++++
.../internal/mem/unsafe/UnsafeMemoryProvider.java | 36 ++--
.../IgniteCacheDatabaseSharedManager.java | 8 +-
.../org/apache/ignite/mem/MemoryAllocator.java | 36 ++++
modules/numa-allocator/README.md | 141 ++++++++++++++
modules/numa-allocator/pom.xml | 210 ++++++++++++++++++++
modules/numa-allocator/src/main/cpp/CMakeLists.txt | 45 +++++
.../src/main/cpp/include/numa/numa_alloc.h | 110 +++++++++++
.../org_apache_ignite_internal_mem_NumaAllocUtil.h | 38 ++++
.../src/main/cpp/src/numa/numa_alloc.cpp | 214 +++++++++++++++++++++
...rg_apache_ignite_internal_mem_NumaAllocUtil.cpp | 129 +++++++++++++
.../apache/ignite/internal/mem/NumaAllocUtil.java | 151 +++++++++++++++
.../mem/InterleavedNumaAllocationStrategy.java | 78 ++++++++
.../ignite/mem/LocalNumaAllocationStrategy.java | 44 +++++
.../apache/ignite/mem/NumaAllocationStrategy.java | 30 +++
.../java/org/apache/ignite/mem/NumaAllocator.java | 53 +++++
.../ignite/mem/SimpleNumaAllocationStrategy.java | 77 ++++++++
.../internal/mem/NumaAllocatorBasicTest.java | 177 +++++++++++++++++
.../ignite/internal/mem/NumaAllocatorUnitTest.java | 121 ++++++++++++
.../ignite/testsuites/NumaAllocatorTestSuite.java | 32 +++
parent/pom.xml | 4 +
pom.xml | 8 +
25 files changed, 1822 insertions(+), 16 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index d073814..837f7c4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,7 +21,10 @@ matrix:
include:
- language: java
os: linux
- dist: xenial
+ dist: bionic
+ before_install:
+ - sudo apt-get update
+ - sudo apt-get -y install libnuma-dev
install: skip
jdk: openjdk8
before_script:
@@ -32,7 +35,10 @@ matrix:
- language: java
os: linux
- dist: xenial
+ dist: bionic
+ before_install:
+ - sudo apt-get update
+ - sudo apt-get -y install libnuma-dev
install: skip
jdk: openjdk11
before_script:
@@ -58,7 +64,10 @@ matrix:
- language: java
name: "Check test suites"
os: linux
- dist: xenial
+ dist: bionic
+ before_install:
+ - sudo apt-get update
+ - sudo apt-get -y install libnuma-dev
install: skip
jdk: openjdk8
script: mvn test -Pcheck-test-suites,all-java,all-scala,scala -B -V
diff --git
a/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
b/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
index 3f3b09a..2865bab4 100644
---
a/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
+++
b/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
@@ -20,6 +20,7 @@ import java.io.Serializable;
import org.apache.ignite.DataRegionMetrics;
import org.apache.ignite.internal.mem.IgniteOutOfMemoryException;
import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.mem.MemoryAllocator;
import org.apache.ignite.mxbean.DataRegionMetricsMXBean;
import org.apache.ignite.mxbean.MetricsMxBean;
import org.jetbrains.annotations.Nullable;
@@ -152,6 +153,9 @@ public final class DataRegionConfiguration implements
Serializable {
/** Warm-up configuration. */
@Nullable private WarmUpConfiguration warmUpCfg;
+ /** Memory allocator. */
+ @Nullable private MemoryAllocator memoryAllocator = null;
+
/**
* Gets data region name.
*
@@ -245,6 +249,25 @@ public final class DataRegionConfiguration implements
Serializable {
}
/**
+ * @return Memory allocator instance.
+ */
+ @Nullable public MemoryAllocator getMemoryAllocator() {
+ return memoryAllocator;
+ }
+
+ /**
+ * Sets memory allocator. If not specified, default, based on {@code
Unsafe} allocator will be used.
+ *
+ * @param allocator Memory allocator instance.
+ * @return {@code this} for chaining.
+ */
+ public DataRegionConfiguration setMemoryAllocator(MemoryAllocator
allocator) {
+ memoryAllocator = allocator;
+
+ return this;
+ }
+
+ /**
* Gets memory pages eviction mode. If {@link
DataPageEvictionMode#DISABLED} is used (default) then an out of
* memory exception will be thrown if the memory region usage, defined by
this data region, goes beyond its
* capacity which is {@link #getMaxSize()}.
diff --git
a/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
b/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
index 7a47faa..6e4801b 100644
---
a/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
+++
b/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
@@ -28,6 +28,7 @@ import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteExperimental;
+import org.apache.ignite.mem.MemoryAllocator;
import org.apache.ignite.mxbean.MetricsMxBean;
import org.jetbrains.annotations.Nullable;
@@ -337,6 +338,9 @@ public class DataStorageConfiguration implements
Serializable {
/** Minimum size of wal archive folder, in bytes. */
private long minWalArchiveSize = HALF_MAX_WAL_ARCHIVE_SIZE;
+ /** Default memory allocator for all data regions. */
+ @Nullable private MemoryAllocator memoryAllocator = null;
+
/**
* Creates valid durable memory configuration with all default values.
*/
@@ -1364,6 +1368,27 @@ public class DataStorageConfiguration implements
Serializable {
return this;
}
+ /**
+ * @return Memory allocator instance.
+ */
+ @Nullable public MemoryAllocator getMemoryAllocator() {
+ return memoryAllocator;
+ }
+
+ /**
+ * Sets default memory allocator for all memory regions. If not specified,
default, based on {@code Unsafe}
+ * allocator will be used. Allocator can be overrided for data region using
+ * {@link DataRegionConfiguration#setMemoryAllocator(MemoryAllocator)}
+ *
+ * @param allocator Memory allocator instance.
+ * @return {@code this} for chaining.
+ */
+ public DataStorageConfiguration setMemoryAllocator(MemoryAllocator
allocator) {
+ memoryAllocator = allocator;
+
+ return this;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(DataStorageConfiguration.class, this);
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/mem/unsafe/UnsafeMemoryAllocator.java
b/modules/core/src/main/java/org/apache/ignite/internal/mem/unsafe/UnsafeMemoryAllocator.java
new file mode 100644
index 0000000..2d80390
--- /dev/null
+++
b/modules/core/src/main/java/org/apache/ignite/internal/mem/unsafe/UnsafeMemoryAllocator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.internal.mem.unsafe;
+
+import org.apache.ignite.internal.util.GridUnsafe;
+import org.apache.ignite.mem.MemoryAllocator;
+
+/** */
+public class UnsafeMemoryAllocator implements MemoryAllocator {
+ /** {@inheritDoc} */
+ @Override public long allocateMemory(long size) {
+ return GridUnsafe.allocateMemory(size);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void freeMemory(long addr) {
+ GridUnsafe.freeMemory(addr);
+ }
+}
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/mem/unsafe/UnsafeMemoryProvider.java
b/modules/core/src/main/java/org/apache/ignite/internal/mem/unsafe/UnsafeMemoryProvider.java
index 8cb8119..d281912 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/mem/unsafe/UnsafeMemoryProvider.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/mem/unsafe/UnsafeMemoryProvider.java
@@ -25,8 +25,9 @@ import org.apache.ignite.IgniteLogger;
import org.apache.ignite.internal.mem.DirectMemoryProvider;
import org.apache.ignite.internal.mem.DirectMemoryRegion;
import org.apache.ignite.internal.mem.UnsafeChunk;
-import org.apache.ignite.internal.util.GridUnsafe;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.mem.MemoryAllocator;
+import org.jetbrains.annotations.Nullable;
/**
* Memory provider implementation based on unsafe memory access.
@@ -49,10 +50,22 @@ public class UnsafeMemoryProvider implements
DirectMemoryProvider {
/** */
private int used = 0;
+ /** */
+ private final MemoryAllocator allocator;
+
+ /**
+ * @param log Ignite logger to use.
+ */
+ public UnsafeMemoryProvider(@Nullable IgniteLogger log) {
+ this(log, null);
+ }
+
/**
* @param log Ignite logger to use.
+ * @param allocator Memory allocator. If {@code null}, default {@link
UnsafeMemoryAllocator} will be used.
*/
- public UnsafeMemoryProvider(IgniteLogger log) {
+ public UnsafeMemoryProvider(@Nullable IgniteLogger log, @Nullable
MemoryAllocator allocator) {
+ this.allocator = allocator == null ? new UnsafeMemoryAllocator() :
allocator;
this.log = log;
}
@@ -70,20 +83,21 @@ public class UnsafeMemoryProvider implements
DirectMemoryProvider {
/** {@inheritDoc} */
@Override public void shutdown(boolean deallocate) {
+ if (!deallocate) {
+ used = 0;
+
+ return;
+ }
+
if (regions != null) {
for (Iterator<DirectMemoryRegion> it = regions.iterator();
it.hasNext(); ) {
DirectMemoryRegion chunk = it.next();
- if (deallocate) {
- GridUnsafe.freeMemory(chunk.address());
+ allocator.freeMemory(chunk.address());
- // Safety.
- it.remove();
- }
+ // Safety.
+ it.remove();
}
-
- if (!deallocate)
- used = 0;
}
}
@@ -100,7 +114,7 @@ public class UnsafeMemoryProvider implements
DirectMemoryProvider {
long ptr;
try {
- ptr = GridUnsafe.allocateMemory(chunkSize);
+ ptr = allocator.allocateMemory(chunkSize);
}
catch (IllegalArgumentException e) {
String msg = "Failed to allocate next memory chunk: " +
U.readableSize(chunkSize, true) +
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
index e8a6da0..0742afd 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
@@ -1276,7 +1276,11 @@ public class IgniteCacheDatabaseSharedManager extends
GridCacheSharedManagerAdap
boolean trackable,
PageReadWriteManager pmPageMgr
) throws IgniteCheckedException {
- PageMemory pageMem =
createPageMemory(createOrReuseMemoryProvider(plcCfg), memCfg, plcCfg,
memMetrics, trackable, pmPageMgr);
+ if (plcCfg.getMemoryAllocator() == null)
+ plcCfg.setMemoryAllocator(memCfg.getMemoryAllocator());
+
+ PageMemory pageMem =
createPageMemory(createOrReuseMemoryProvider(plcCfg), memCfg, plcCfg,
memMetrics,
+ trackable, pmPageMgr);
return new DataRegion(pageMem, plcCfg, memMetrics,
createPageEvictionTracker(plcCfg, pageMem));
}
@@ -1315,7 +1319,7 @@ public class IgniteCacheDatabaseSharedManager extends
GridCacheSharedManagerAdap
File allocPath = buildAllocPath(plcCfg);
return allocPath == null ?
- new UnsafeMemoryProvider(log) :
+ new UnsafeMemoryProvider(log, plcCfg.getMemoryAllocator()) :
new MappedFileMemoryProvider(
log,
allocPath);
diff --git
a/modules/core/src/main/java/org/apache/ignite/mem/MemoryAllocator.java
b/modules/core/src/main/java/org/apache/ignite/mem/MemoryAllocator.java
new file mode 100644
index 0000000..0d68739
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/mem/MemoryAllocator.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.mem;
+
+/**
+ * Base interface for offheap memory allocator.
+ */
+public interface MemoryAllocator {
+ /**
+ * @param size Size of allocated memory.
+ *
+ * @return Pointer to memory or {@code 0} if failed.
+ */
+ public long allocateMemory(long size);
+
+ /**
+ * Deallocates memory.
+ *
+ * @param addr Address of memory.
+ */
+ public void freeMemory(long addr);
+}
diff --git a/modules/numa-allocator/README.md b/modules/numa-allocator/README.md
new file mode 100644
index 0000000..89abbc4
--- /dev/null
+++ b/modules/numa-allocator/README.md
@@ -0,0 +1,141 @@
+# NUMA aware memory allocator for Apache Ignite
+Allocates memory on linux using `libnuma` under the hood.
+
+## Requirements
+`libnuma 2.0.x` must be installed on your Linux machine. It is recommended to
install `numactl` utility also.
+1. `RHEL` or `Cent OS`
+```bash
+$ sudo yum install numactl
+```
+2. `Ubuntu` or `Debian`
+```bash
+$ sudo apt install numactl
+```
+
+## Building from source
+`JDK`, `C++ 11` compatible compiler (`gcc >= 4.8.5`) and `libnuma` headers
+1. `RHEL` or `Cent OS`
+```bash
+$ sudo yum groupinstall 'Development Tools'
+$ sudo yum install java-1.8.0-openjdk numactl-devel libstdc++-static
+```
+2. `Ubuntu` or `Debian`
+```bash
+$ sudo apt install build-essential libnuma-dev openjdk-8-jdk
+```
+## Usage
+### Simple allocation strategy
+1. Allocation with default NUMA policy for thread/process, uses `void
*numa_alloc(size_t)` under the hood:
+```xml
+<property name="dataStorageConfiguration">
+ <bean class="org.apache.ignite.configuration.DataStorageConfiguration">
+ <property name="defaultDataRegionConfiguration">
+ <bean
class="org.apache.ignite.configuration.DataRegionConfiguration">
+ <property name="name" value="Default_Region"/>
+ ....
+ <property name="memoryAllocator">
+ <bean class="org.apache.ignite.mem.NumaAllocator">
+ <constructor-arg>
+ <bean
class="org.apache.ignite.mem.SimpleNumaAllocationStrategy"/>
+ </constructor-arg>
+ </bean>
+ </property>
+ </bean>
+ </property>
+ </bean>
+</property>
+```
+2. Allocation on specific NUMA node, uses `void *numa_alloc_onnode(size_t,
int)` under the hood:
+```xml
+<property name="dataStorageConfiguration">
+ <bean class="org.apache.ignite.configuration.DataStorageConfiguration">
+ <property name="defaultDataRegionConfiguration">
+ <bean
class="org.apache.ignite.configuration.DataRegionConfiguration">
+ <property name="name" value="Default_Region"/>
+ ....
+ <property name="memoryAllocator">
+ <bean class="org.apache.ignite.mem.NumaAllocator">
+ <constructor-arg>
+ <bean
class="org.apache.ignite.mem.SimpleNumaAllocationStrategy">
+ <constructor-arg name="node" value="0"/>
+ </bean>
+ </constructor-arg>
+ </bean>
+ </property>
+ </bean>
+ </property>
+ </bean>
+</property>
+```
+### Interleaved allocation strategy.
+1. Interleaved allocation on all NUMA nodes, uses `void
*numa_alloc_interleaved(size_t)` under the hood:
+```xml
+<property name="dataStorageConfiguration">
+ <bean class="org.apache.ignite.configuration.DataStorageConfiguration">
+ <property name="defaultDataRegionConfiguration">
+ <bean
class="org.apache.ignite.configuration.DataRegionConfiguration">
+ <property name="name" value="Default_Region"/>
+ ....
+ <property name="memoryAllocator">
+ <bean class="org.apache.ignite.mem.NumaAllocator">
+ <constructor-arg>
+ <bean
class="org.apache.ignite.mem.InterleavedNumaAllocationStrategy"/>
+ </constructor-arg>
+ </bean>
+ </property>
+ </bean>
+ </property>
+ </bean>
+</property>
+```
+2. Interleaved allocation on specified NUMA nodes, uses `void
*numa_alloc_interleaved_subset(size_t, struct bitmask*)`
+under the hood:
+```xml
+<property name="dataStorageConfiguration">
+ <bean class="org.apache.ignite.configuration.DataStorageConfiguration">
+ <property name="defaultDataRegionConfiguration">
+ <bean
class="org.apache.ignite.configuration.DataRegionConfiguration">
+ <property name="name" value="Default_Region"/>
+ ....
+ <property name="memoryAllocator">
+ <bean class="org.apache.ignite.mem.NumaAllocator">
+ <constructor-arg>
+ <bean
class="org.apache.ignite.mem.InterleavedNumaAllocationStrategy">
+ <constructor-arg name="nodes">
+ <array>
+ <value>0</value>
+ <value>1</value>
+ </array>
+ </constructor-arg>
+ </bean>
+ </constructor-arg>
+ </bean>
+ </property>
+ </bean>
+ </property>
+ </bean>
+</property>
+```
+## Local node allocation strategy
+Allocation on local for process NUMA node, uses `void*
numa_alloc_onnode(size_t)` under the hood.
+```xml
+<property name="dataStorageConfiguration">
+ <bean class="org.apache.ignite.configuration.DataStorageConfiguration">
+ <property name="defaultDataRegionConfiguration">
+ <bean
class="org.apache.ignite.configuration.DataRegionConfiguration">
+ <property name="name" value="Default_Region"/>
+ ....
+ <property name="memoryAllocator">
+ <bean class="org.apache.ignite.mem.NumaAllocator">
+ <constructor-arg>
+ <constructor-arg>
+ <bean
class="org.apache.ignite.mem.LocalNumaAllocationStrategy"/>
+ </constructor-arg>
+ </constructor-arg>
+ </bean>
+ </property>
+ </bean>
+ </property>
+ </bean>
+</property>
+```
diff --git a/modules/numa-allocator/pom.xml b/modules/numa-allocator/pom.xml
new file mode 100644
index 0000000..830330a
--- /dev/null
+++ b/modules/numa-allocator/pom.xml
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ POM file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-parent</artifactId>
+ <version>1</version>
+ <relativePath>../../parent</relativePath>
+ </parent>
+
+ <artifactId>ignite-numa-allocator</artifactId>
+ <version>${revision}</version>
+ <url>http://ignite.apache.org</url>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-core</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-tools</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <version>${spring.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <version>${spring.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.thoughtworks.xstream</groupId>
+ <artifactId>xstream</artifactId>
+ <version>1.4.8</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>linux-build</id>
+ <activation>
+ <os>
+ <name>Linux</name>
+ <arch>amd64</arch>
+ </os>
+ </activation>
+ <properties>
+ <cmake.classifier>linux-x86_64</cmake.classifier>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.googlecode.cmake-maven-project</groupId>
+ <artifactId>cmake-maven-plugin</artifactId>
+ <version>3.7.2-b1</version>
+ <executions>
+ <execution>
+ <id>cmake-generate</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ <configuration>
+
<classifier>${cmake.classifier}</classifier>
+
<sourcePath>${basedir}/src/main/cpp</sourcePath>
+
<targetPath>${project.build.directory}/cppbuild</targetPath>
+ <generator>Unix Makefiles</generator>
+ <options>
+
<option>-DCMAKE_BUILD_TYPE=Release</option>
+ </options>
+ </configuration>
+ </execution>
+ <execution>
+ <id>cmake-build</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <configuration>
+
<classifier>${cmake.classifier}</classifier>
+
<projectDirectory>${project.build.directory}/cppbuild</projectDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-sources</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+
<outputDirectory>${project.build.directory}/classes/org/apache/ignite/internal/mem/linux/${os.arch}/</outputDirectory>
+ <resources>
+ <resource>
+
<directory>${project.build.directory}/cppbuild</directory>
+ <includes>
+ <include>*.so</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-libs</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ <configuration>
+ <excludeTransitive>false</excludeTransitive>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.8.2</version>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/modules/numa-allocator/src/main/cpp/CMakeLists.txt
b/modules/numa-allocator/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..48b42fa
--- /dev/null
+++ b/modules/numa-allocator/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,45 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+cmake_minimum_required(VERSION 3.6)
+project(numa_alloc)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_BUILD_TYPE Release)
+set(CMAKE_CXX_FLAGS "-shared -D_FORTIFY_SOURCE=2 -z noexecstack -z,relro
-z,now -fPIC -Wformat -Wformat-security -Werror=format-security")
+set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pie")
+
+find_package(JNI REQUIRED)
+find_package(Threads REQUIRED)
+include_directories(${CMAKE_THREAD_LIBS_INIT})
+
+find_library(NUMA_LIBRARIES NAMES libnuma.so)
+if (NOT NUMA_LIBRARIES)
+ message(FATAL_ERROR "not found numa library")
+endif (NOT NUMA_LIBRARIES)
+
+include_directories(${JAVA_INCLUDE_PATH})
+include_directories(${JAVA_INCLUDE_PATH2})
+include_directories(include)
+
+file(GLOB SOURCES
+ src/numa/numa_alloc.cpp
+ src/org_apache_ignite_internal_mem_NumaAllocUtil.cpp
+)
+add_library(numa_alloc SHARED ${SOURCES})
+message(STATUS "NUMA_LIBRARIES: ${NUMA_LIBRARIES}")
+target_link_libraries(numa_alloc PRIVATE -static-libgcc -static-libstdc++
${NUMA_LIBRARIES})
diff --git a/modules/numa-allocator/src/main/cpp/include/numa/numa_alloc.h
b/modules/numa-allocator/src/main/cpp/include/numa/numa_alloc.h
new file mode 100644
index 0000000..b116a40
--- /dev/null
+++ b/modules/numa-allocator/src/main/cpp/include/numa/numa_alloc.h
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NUMA_ALLOC_H
+#define _NUMA_ALLOC_H
+
+#include <ostream>
+
+namespace numa {
+ class BitSet {
+ public:
+ BitSet();
+
+ BitSet(const BitSet &other);
+
+ BitSet &operator=(const BitSet &other);
+
+ ~BitSet();
+
+ class Reference {
+ public:
+ explicit Reference(BitSet &bitset, size_t pos) : bitset(&bitset),
pos(pos) {}
+
+ Reference &operator=(bool value) {
+ bitset->Set(pos, value);
+ return *this;
+ }
+
+ Reference &operator=(const Reference &other) {
+ if (&other != this) {
+ bitset->Set(pos, other.bitset->Get(other.pos));
+ }
+ return *this;
+ }
+
+ explicit operator bool() const {
+ return bitset->Get(pos);
+ }
+
+ private:
+ BitSet *bitset;
+ size_t pos;
+ };
+
+ Reference operator[](size_t pos) {
+ return Reference(*this, pos);
+ }
+
+ bool operator[](size_t pos) const {
+ return this->Get(pos);
+ }
+
+ bool operator==(const BitSet &other);
+
+ bool operator!=(const BitSet &other);
+
+ friend void *AllocInterleaved(size_t size, const BitSet &node_set);
+
+ void Set(size_t pos, bool value = true);
+
+ void Set();
+
+ void Reset(size_t pos);
+
+ void Reset();
+
+ size_t Size() const;
+
+ friend std::ostream &operator<<(std::ostream &os, const BitSet &set);
+
+ private:
+ bool Get(size_t pos) const;
+
+ class BitSetImpl;
+
+ BitSetImpl *p_impl_;
+ };
+
+ int NumaNodesCount();
+
+ void *Alloc(size_t size);
+
+ void *Alloc(size_t size, int node);
+
+ void *AllocLocal(size_t size);
+
+ void *AllocInterleaved(size_t size);
+
+ void *AllocInterleaved(size_t size, const BitSet &node_set);
+
+ size_t Size(void *ptr);
+
+ void Free(void *ptr);
+}
+
+#endif //_NUMA_ALLOC_H
diff --git
a/modules/numa-allocator/src/main/cpp/include/org_apache_ignite_internal_mem_NumaAllocUtil.h
b/modules/numa-allocator/src/main/cpp/include/org_apache_ignite_internal_mem_NumaAllocUtil.h
new file mode 100644
index 0000000..3a7bbb5
--- /dev/null
+++
b/modules/numa-allocator/src/main/cpp/include/org_apache_ignite_internal_mem_NumaAllocUtil.h
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ORG_APACHE_IGNITE_INTERNAL_MEM_NUMAALLOCUTIL_H
+#define _ORG_APACHE_IGNITE_INTERNAL_MEM_NUMAALLOCUTIL_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jlong JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_allocate(JNIEnv *, jclass,
jlong);
+JNIEXPORT jlong JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_allocateOnNode(JNIEnv *,
jclass, jlong, jint);
+JNIEXPORT jlong JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_allocateLocal(JNIEnv *,
jclass, jlong);
+JNIEXPORT jlong JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_allocateInterleaved(JNIEnv *,
jclass, jlong, jintArray);
+JNIEXPORT jlong JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_chunkSize(JNIEnv *, jclass,
jlong);
+JNIEXPORT void JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_free(JNIEnv *, jclass, jlong);
+JNIEXPORT jint JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_nodesCount(JNIEnv *, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // _ORG_APACHE_IGNITE_INTERNAL_MEM_NUMAALLOCUTIL_H
diff --git a/modules/numa-allocator/src/main/cpp/src/numa/numa_alloc.cpp
b/modules/numa-allocator/src/main/cpp/src/numa/numa_alloc.cpp
new file mode 100644
index 0000000..4194978
--- /dev/null
+++ b/modules/numa-allocator/src/main/cpp/src/numa/numa_alloc.cpp
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <numa.h>
+#include <numa/numa_alloc.h>
+
+namespace numa {
+ class BitSet::BitSetImpl {
+ public:
+ BitSetImpl() : size_(numa_max_node() + 1) {
+ mask_ = numa_bitmask_alloc(size_);
+ }
+
+ BitSetImpl(const BitSetImpl &other) : size_(other.size_) {
+ mask_ = numa_bitmask_alloc(size_);
+ copy_bitmask_to_bitmask(other.mask_, mask_);
+ }
+
+ void SetBit(size_t idx, bool val) {
+ if (idx < size_) {
+ if (val)
+ numa_bitmask_setbit(mask_, idx);
+ else
+ numa_bitmask_clearbit(mask_, idx);
+ }
+ }
+
+ void SetAll(bool val) {
+ if (val)
+ numa_bitmask_setall(mask_);
+ else
+ numa_bitmask_clearall(mask_);
+ }
+
+ bool GetBit(size_t idx) const {
+ if (idx < size_)
+ return numa_bitmask_isbitset(mask_, idx);
+ else
+ return false;
+ }
+
+ bool Equals(const BitSetImpl &other) const {
+ return numa_bitmask_equal(mask_, other.mask_);
+ }
+
+ size_t Size() const {
+ return size_;
+ }
+
+ bitmask *Raw() {
+ return mask_;
+ }
+
+ ~BitSetImpl() {
+ numa_bitmask_free(mask_);
+ }
+
+ private:
+ bitmask *mask_;
+ size_t size_;
+ };
+
+ BitSet::BitSet() {
+ p_impl_ = new BitSetImpl();
+ }
+
+ BitSet::BitSet(const BitSet &other) {
+ p_impl_ = new BitSetImpl(*other.p_impl_);
+ }
+
+ BitSet &BitSet::operator=(const BitSet &other) {
+ if (this != &other) {
+ BitSet tmp(other);
+ std::swap(this->p_impl_, tmp.p_impl_);
+ }
+ return *this;
+ }
+
+ bool BitSet::Get(size_t pos) const {
+ return p_impl_->GetBit(pos);
+ }
+
+ void BitSet::Set(size_t pos, bool value) {
+ p_impl_->SetBit(pos, value);
+ }
+
+ void BitSet::Set() {
+ p_impl_->SetAll(true);
+ }
+
+ void BitSet::Reset(size_t pos) {
+ p_impl_->SetBit(pos, false);
+ }
+
+ void BitSet::Reset() {
+ p_impl_->SetAll(false);
+ }
+
+ size_t BitSet::Size() const {
+ return p_impl_->Size();
+ }
+
+ bool BitSet::operator==(const BitSet &other) {
+ return this->p_impl_->Equals(*other.p_impl_);
+ }
+
+ bool BitSet::operator!=(const BitSet &other) {
+ return !(*this == other);
+ }
+
+ BitSet::~BitSet() {
+ delete p_impl_;
+ }
+
+ std::ostream &operator<<(std::ostream &os, const BitSet &set) {
+ os << '{';
+ for (size_t i = 0; i < set.Size(); ++i) {
+ os << set[i];
+ if (i < set.Size() - 1) {
+ os << ", ";
+ }
+ }
+ os << '}';
+ return os;
+ }
+
+ int NumaNodesCount() {
+ return numa_max_node() + 1;
+ }
+
+ /**
+ * Memory layout:
+ * +-------------------------------+------------------------+
+ * | Header (sizeof(max_align_t)) | Application memory |
+ * +------------------------------ +------------------------+
+ * ^
+ * |
+ * Result pointer
+ * Size of application memory chunk is written to header.
+ * Total allocated size equals to size of application memory chunk plus
sizeof(max_align_t).
+ */
+ union region_size {
+ size_t size;
+ max_align_t a;
+ };
+
+ template<typename Func, typename ...Args>
+ inline void *NumaAllocHelper(Func f, size_t size, Args ...args) {
+ auto ptr = static_cast<region_size *>(f(size + sizeof(region_size),
args...));
+ if (ptr) {
+ ptr->size = size;
+ ptr++;
+ }
+ return ptr;
+ }
+
+ inline region_size* ConvertPointer(void* buf) {
+ if (buf) {
+ auto *ptr = static_cast<region_size *>(buf);
+ ptr--;
+ return ptr;
+ }
+ return nullptr;
+ }
+
+ void *Alloc(size_t size) {
+ return NumaAllocHelper(numa_alloc, size);
+ }
+
+ void *Alloc(size_t size, int node) {
+ return NumaAllocHelper(numa_alloc_onnode, size, node);
+ }
+
+ void *AllocLocal(size_t size) {
+ return NumaAllocHelper(numa_alloc_local, size);
+ }
+
+ void *AllocInterleaved(size_t size) {
+ return NumaAllocHelper(numa_alloc_interleaved, size);
+ }
+
+ void *AllocInterleaved(size_t size, const BitSet &node_set) {
+ return NumaAllocHelper(numa_alloc_interleaved_subset, size,
node_set.p_impl_->Raw());
+ }
+
+ size_t Size(void *buf) {
+ auto ptr = ConvertPointer(buf);
+ if (ptr) {
+ return ptr->size;
+ }
+ return 0;
+ }
+
+ void Free(void *buf) {
+ auto ptr = ConvertPointer(buf);
+ if (ptr) {
+ numa_free(ptr, ptr->size + sizeof(region_size));
+ }
+ }
+}
diff --git
a/modules/numa-allocator/src/main/cpp/src/org_apache_ignite_internal_mem_NumaAllocUtil.cpp
b/modules/numa-allocator/src/main/cpp/src/org_apache_ignite_internal_mem_NumaAllocUtil.cpp
new file mode 100644
index 0000000..38ad95b
--- /dev/null
+++
b/modules/numa-allocator/src/main/cpp/src/org_apache_ignite_internal_mem_NumaAllocUtil.cpp
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <numa/numa_alloc.h>
+#include "org_apache_ignite_internal_mem_NumaAllocUtil.h"
+
+class JIntArrayGuard {
+public:
+ JIntArrayGuard(JNIEnv* env, jintArray arr): env_(env), java_arr_(arr) {
+ arr_ = env->GetIntArrayElements(java_arr_, nullptr);
+ length_ = static_cast<size_t>(env->GetArrayLength(java_arr_));
+ }
+
+ JIntArrayGuard() = delete;
+ JIntArrayGuard(const JIntArrayGuard&) = delete;
+ JIntArrayGuard& operator=(const JIntArrayGuard&) = delete;
+
+ ~JIntArrayGuard() {
+ env_->ReleaseIntArrayElements(java_arr_, arr_, 0);
+ }
+
+ const int& operator[](size_t pos) const {
+ return arr_[pos];
+ }
+
+ int& operator[](size_t pos) {
+ return arr_[pos];
+ }
+
+ size_t Size() const {
+ return length_;
+ }
+private:
+ JNIEnv* env_;
+ jintArray java_arr_;
+ jint* arr_;
+ size_t length_;
+};
+
+JNIEXPORT jlong JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_allocate(
+ JNIEnv*,
+ jclass,
+ jlong size
+) {
+ void* ptr = numa::Alloc(static_cast<size_t>(size));
+ return reinterpret_cast<jlong>(ptr);
+}
+
+JNIEXPORT jlong JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_allocateOnNode(
+ JNIEnv*,
+ jclass,
+ jlong size,
+ jint node
+) {
+ void* ptr;
+ auto size_ = static_cast<size_t>(size);
+ if (node >= 0 && node < numa::NumaNodesCount()) {
+ ptr = numa::Alloc(size_, static_cast<int>(node));
+ }
+ else {
+ ptr = numa::Alloc(size_);
+ }
+ return reinterpret_cast<jlong>(ptr);
+}
+
+JNIEXPORT jlong JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_allocateLocal(
+ JNIEnv*,
+ jclass,
+ jlong size
+) {
+ void* ptr = numa::AllocLocal(static_cast<size_t>(size));
+ return reinterpret_cast<jlong>(ptr);
+}
+
+JNIEXPORT jlong JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_allocateInterleaved(
+ JNIEnv *jniEnv,
+ jclass,
+ jlong size,
+ jintArray arr
+) {
+ void* ptr;
+ auto size_ = static_cast<size_t>(size);
+ if (arr != nullptr) {
+ JIntArrayGuard nodes(jniEnv, arr);
+
+ if (nodes.Size() > 0) {
+ numa::BitSet bs;
+ for (size_t i = 0; i < nodes.Size(); ++i) {
+ bs.Set(nodes[i]);
+ }
+ ptr = numa::AllocInterleaved(size_, bs);
+ }
+ else {
+ ptr = numa::AllocInterleaved(size_);
+ }
+ }
+ else {
+ ptr = numa::AllocInterleaved(size_);
+ }
+ return reinterpret_cast<jlong>(ptr);
+}
+
+JNIEXPORT jlong JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_chunkSize(JNIEnv *, jclass,
jlong addr) {
+ void* ptr = reinterpret_cast<void*>(addr);
+ return static_cast<jlong>(numa::Size(ptr));
+}
+
+JNIEXPORT void JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_free(JNIEnv *, jclass, jlong
addr) {
+ void* ptr = reinterpret_cast<void*>(addr);
+ numa::Free(ptr);
+}
+
+JNIEXPORT jint JNICALL
Java_org_apache_ignite_internal_mem_NumaAllocUtil_nodesCount(JNIEnv *, jclass) {
+ return static_cast<jint>(numa::NumaNodesCount());
+}
diff --git
a/modules/numa-allocator/src/main/java/org/apache/ignite/internal/mem/NumaAllocUtil.java
b/modules/numa-allocator/src/main/java/org/apache/ignite/internal/mem/NumaAllocUtil.java
new file mode 100644
index 0000000..18f753f
--- /dev/null
+++
b/modules/numa-allocator/src/main/java/org/apache/ignite/internal/mem/NumaAllocUtil.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.mem;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Paths;
+
+/** */
+public class NumaAllocUtil {
+ /** */
+ private static final String extension = ".so";
+
+ /** */
+ private static final String libraryName = "libnuma_alloc";
+
+ /** */
+ private NumaAllocUtil() {
+ // No-op.
+ }
+
+ /** */
+ private static String getLibName() {
+ return Paths.get("/org/apache/ignite/internal/mem", getOSName(),
getOSArch(), libraryName + extension)
+ .toString();
+ }
+
+ /** */
+ private static String getOSArch() {
+ return System.getProperty("os.arch", "");
+ }
+
+ /** */
+ private static String getOSName() {
+ if (System.getProperty("os.name", "").contains("Linux"))
+ return "linux";
+ else
+ throw new UnsupportedOperationException("Operating System is not
supported");
+ }
+
+ static {
+ String libName = getLibName();
+ File nativeLib = null;
+
+ try (InputStream in =
NumaAllocUtil.class.getResourceAsStream(libName)) {
+ if (in == null) {
+ throw new ExceptionInInitializerError("Failed to load native
numa_alloc library, "
+ + libName + " not found");
+ }
+
+ nativeLib = File.createTempFile(libraryName, extension);
+
+ try (FileOutputStream out = new FileOutputStream(nativeLib)) {
+ byte[] buf = new byte[4096];
+
+ int bytesRead;
+ while ((bytesRead = in.read(buf)) > 0)
+ out.write(buf, 0, bytesRead);
+ }
+
+ System.load(nativeLib.getAbsolutePath());
+
+ NUMA_NODES_CNT = nodesCount();
+ }
+ catch (IOException | UnsatisfiedLinkError e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ finally {
+ if (nativeLib != null)
+ nativeLib.deleteOnExit();
+ }
+ }
+
+ /** */
+ public static final int NUMA_NODES_CNT;
+
+ /**
+ * Allocate memory using using default NUMA memory policy of current
thread.
+ * Uses {@code void *numa_alloc(size_t)} under the hood.
+ * <p>
+ * @param size Size of buffer.
+ * @return Address of buffer.
+ */
+ public static native long allocate(long size);
+
+ /**
+ * Allocate memory on specific NUMA node. Uses {@code void
*numa_alloc_onnode(size_t)} under the hood.
+ * <p>
+ * @param size Size of buffer.
+ * @param node NUMA node.
+ * @return Address of buffer.
+ */
+ public static native long allocateOnNode(long size, int node);
+
+ /**
+ * Allocate memory on local NUMA node. Uses {@code void
*numa_alloc_local(size_t)} under the hood.
+ * <p>
+ * @param size Size of buffer.
+ * @return Address of buffer.
+ */
+ public static native long allocateLocal(long size);
+
+ /**
+ * Allocate memory interleaved on NUMA nodes. Uses
+ * {@code void *numa_alloc_interleaved_subset(size_t, struct bitmask*)}
under the hood.
+ * <p>
+ * @param size Size of buffer.
+ * @param nodes Array of nodes allocate on. If {@code null} or empty,
allocate on all NODES.
+ * @return Address of buffer.
+ */
+ public static native long allocateInterleaved(long size, int[] nodes);
+
+ /**
+ * Get allocated buffer size.
+ *
+ * @param addr Address of buffer.
+ * @return Size of buffer.
+ */
+ public static native long chunkSize(long addr);
+
+ /**
+ * Free allocated memory.
+ *
+ * @param addr Address of buffer.
+ */
+ public static native void free(long addr);
+
+ /**
+ * Get NUMA nodes count.
+ *
+ * @return NUMA nodes count available on system.
+ */
+ private static native int nodesCount();
+}
diff --git
a/modules/numa-allocator/src/main/java/org/apache/ignite/mem/InterleavedNumaAllocationStrategy.java
b/modules/numa-allocator/src/main/java/org/apache/ignite/mem/InterleavedNumaAllocationStrategy.java
new file mode 100644
index 0000000..573078c
--- /dev/null
+++
b/modules/numa-allocator/src/main/java/org/apache/ignite/mem/InterleavedNumaAllocationStrategy.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.mem;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import org.apache.ignite.internal.mem.NumaAllocUtil;
+import org.apache.ignite.internal.util.tostring.GridToStringBuilder;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.A;
+
+/**
+ * Interleaved NUMA allocation strategy.
+ * <p>
+ * Use {@link
InterleavedNumaAllocationStrategy#InterleavedNumaAllocationStrategy()} to
allocate memory interleaved
+ * on all available NUMA nodes. Memory will be allocated using {@code void
*numa_alloc_interleaved(size_t)}
+ * of {@code libnuma}.
+ * <p>
+ * Use {@link
InterleavedNumaAllocationStrategy#InterleavedNumaAllocationStrategy(int[])} to
allocate memory interleaved
+ * on specified nodes.
+ * {@code void *numa_alloc_interleaved_subset(size_t, struct bitmask*)} of
{@code lubnuma}.
+ */
+public class InterleavedNumaAllocationStrategy implements
NumaAllocationStrategy, Serializable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ @GridToStringInclude
+ private final int[] nodes;
+
+ /** */
+ public InterleavedNumaAllocationStrategy() {
+ this(null);
+ }
+
+ /**
+ * @param nodes Array of NUMA nodes to allocate on.
+ */
+ public InterleavedNumaAllocationStrategy(int[] nodes) {
+ if (nodes != null && nodes.length > 0) {
+ this.nodes = Arrays.copyOf(nodes, nodes.length);
+
+ Arrays.sort(this.nodes);
+ A.ensure(this.nodes[0] >= 0, "NUMA node number must be positive,
passed instead "
+ + Arrays.toString(this.nodes));
+ A.ensure(this.nodes[this.nodes.length - 1] <
NumaAllocUtil.NUMA_NODES_CNT,
+ "NUMA node number must be less than NUMA_NODES_CNT=" +
NumaAllocUtil.NUMA_NODES_CNT +
+ ", passed instead " + Arrays.toString(this.nodes));
+ }
+ else
+ this.nodes = null;
+ }
+
+ /** {@inheritDoc}*/
+ @Override public long allocateMemory(long size) {
+ return NumaAllocUtil.allocateInterleaved(size, nodes);
+ }
+
+ /** {@inheritDoc}*/
+ @Override public String toString() {
+ return
GridToStringBuilder.toString(InterleavedNumaAllocationStrategy.class, this);
+ }
+}
diff --git
a/modules/numa-allocator/src/main/java/org/apache/ignite/mem/LocalNumaAllocationStrategy.java
b/modules/numa-allocator/src/main/java/org/apache/ignite/mem/LocalNumaAllocationStrategy.java
new file mode 100644
index 0000000..e4e3387
--- /dev/null
+++
b/modules/numa-allocator/src/main/java/org/apache/ignite/mem/LocalNumaAllocationStrategy.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.mem;
+
+import java.io.Serializable;
+import org.apache.ignite.internal.mem.NumaAllocUtil;
+import org.apache.ignite.internal.util.tostring.GridToStringBuilder;
+
+/**
+ * Local NUMA allocation strategy.
+ * <p>
+ * Memory will be allocated using {@code void* numa_alloc_onnode(size_t)} of
{@code lubnuma}.
+ */
+public class LocalNumaAllocationStrategy implements NumaAllocationStrategy,
Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override public long allocateMemory(long size) {
+ return NumaAllocUtil.allocateLocal(size);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return GridToStringBuilder.toString(LocalNumaAllocationStrategy.class,
this);
+ }
+}
diff --git
a/modules/numa-allocator/src/main/java/org/apache/ignite/mem/NumaAllocationStrategy.java
b/modules/numa-allocator/src/main/java/org/apache/ignite/mem/NumaAllocationStrategy.java
new file mode 100644
index 0000000..781bf71
--- /dev/null
+++
b/modules/numa-allocator/src/main/java/org/apache/ignite/mem/NumaAllocationStrategy.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.mem;
+
+/**
+ * Basic interface for allocating memory on NUMA nodes with different policies
using {@code libnuma}.
+ */
+public interface NumaAllocationStrategy {
+ /**
+ * @param size Size of allocated memory.
+ *
+ * @return Pointer to memory.
+ */
+ public long allocateMemory(long size);
+}
diff --git
a/modules/numa-allocator/src/main/java/org/apache/ignite/mem/NumaAllocator.java
b/modules/numa-allocator/src/main/java/org/apache/ignite/mem/NumaAllocator.java
new file mode 100644
index 0000000..8b90862
--- /dev/null
+++
b/modules/numa-allocator/src/main/java/org/apache/ignite/mem/NumaAllocator.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.mem;
+
+import java.io.Serializable;
+import org.apache.ignite.internal.mem.NumaAllocUtil;
+
+/**
+ * NUMA aware memory allocator. Uses {@code libnuma} under the hood. Only
Linux distros with {@code libnuma >= 2.0.x}
+ * are supported.
+ * <p>
+ * Allocation strategy can be defined by setting {@code allocStrategy} to
+ * {@link NumaAllocator#NumaAllocator(NumaAllocationStrategy)}.
+ */
+public class NumaAllocator implements MemoryAllocator, Serializable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private final NumaAllocationStrategy allocStrategy;
+
+ /**
+ * @param allocStrategy Allocation strategy.
+ */
+ public NumaAllocator(NumaAllocationStrategy allocStrategy) {
+ this.allocStrategy = allocStrategy;
+ }
+
+ /** {@inheritDoc}*/
+ @Override public long allocateMemory(long size) {
+ return allocStrategy.allocateMemory(size);
+ }
+
+ /** {@inheritDoc}*/
+ @Override public void freeMemory(long addr) {
+ NumaAllocUtil.free(addr);
+ }
+}
diff --git
a/modules/numa-allocator/src/main/java/org/apache/ignite/mem/SimpleNumaAllocationStrategy.java
b/modules/numa-allocator/src/main/java/org/apache/ignite/mem/SimpleNumaAllocationStrategy.java
new file mode 100644
index 0000000..d74fb7a
--- /dev/null
+++
b/modules/numa-allocator/src/main/java/org/apache/ignite/mem/SimpleNumaAllocationStrategy.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.mem;
+
+import java.io.Serializable;
+import org.apache.ignite.internal.mem.NumaAllocUtil;
+import org.apache.ignite.internal.util.tostring.GridToStringBuilder;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.A;
+
+/**
+ * Simple NUMA allocation strategy.
+ * <p>
+ * Use {@link SimpleNumaAllocationStrategy#SimpleNumaAllocationStrategy(int)}
to allocate memory on specific NUMA node
+ * with number equals to {@code node}. Memory will be allocated using {@code
void *numa_alloc_onnode(size_t, int)}
+ * of {@code libnuma}.
+ * <p>
+ * Use {@link SimpleNumaAllocationStrategy#SimpleNumaAllocationStrategy()} to
allocate memory using default NUMA
+ * memory policy of current thread. Memory will be allocated using {@code void
*numa_alloc(size_t)} of {@code lubnuma}.
+ * Memory policy could be set by running application with {@code numactl}
+ */
+public class SimpleNumaAllocationStrategy implements NumaAllocationStrategy,
Serializable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private static final int NODE_NOT_SET = Integer.MAX_VALUE;
+
+ /** */
+ @GridToStringInclude
+ private final int node;
+
+ /** */
+ public SimpleNumaAllocationStrategy() {
+ node = NODE_NOT_SET;
+ }
+
+ /**
+ * @param node Numa node to allocate on.
+ */
+ public SimpleNumaAllocationStrategy(int node) {
+ A.ensure(node >= 0, "NUMA node number must be positive, passed instead
" + node);
+ A.ensure(node < NumaAllocUtil.NUMA_NODES_CNT,
+ "NUMA node number must be less than NUMA_NODES_CNT=" +
NumaAllocUtil.NUMA_NODES_CNT +
+ ", passed instead " + node);
+
+ this.node = node;
+ }
+
+ /** {@inheritDoc}*/
+ @Override public long allocateMemory(long size) {
+ if (node == NODE_NOT_SET)
+ return NumaAllocUtil.allocate(size);
+
+ return NumaAllocUtil.allocateOnNode(size, node);
+ }
+
+ /** {@inheritDoc}*/
+ @Override public String toString() {
+ return
GridToStringBuilder.toString(SimpleNumaAllocationStrategy.class, this);
+ }
+}
diff --git
a/modules/numa-allocator/src/test/java/org/apache/ignite/internal/mem/NumaAllocatorBasicTest.java
b/modules/numa-allocator/src/test/java/org/apache/ignite/internal/mem/NumaAllocatorBasicTest.java
new file mode 100644
index 0000000..db959d9
--- /dev/null
+++
b/modules/numa-allocator/src/test/java/org/apache/ignite/internal/mem/NumaAllocatorBasicTest.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.mem;
+
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.persistence.DataRegion;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.mem.InterleavedNumaAllocationStrategy;
+import org.apache.ignite.mem.LocalNumaAllocationStrategy;
+import org.apache.ignite.mem.NumaAllocationStrategy;
+import org.apache.ignite.mem.NumaAllocator;
+import org.apache.ignite.mem.SimpleNumaAllocationStrategy;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** */
+@RunWith(Parameterized.class)
+public class NumaAllocatorBasicTest extends GridCommonAbstractTest {
+ /** */
+ private static final long INITIAL_SIZE = 30L * 1024 * 1024;
+
+ /** */
+ private static final long MAX_SIZE = 100L * 1024 * 1024;
+
+ /** */
+ private static final String TEST_CACHE = "test";
+
+ /** */
+ private static final byte[] BUF = new byte[4096];
+
+ /** */
+ private static final int NUM_NODES = 3;
+
+ static {
+ ThreadLocalRandom.current().nextBytes(BUF);
+ }
+
+ /** */
+ @Parameterized.Parameters(name = "allocationStrategy={0},
defaultConfig={1}")
+ public static Iterable<Object[]> data() {
+ return Stream.of(
+ new LocalNumaAllocationStrategy(),
+ new InterleavedNumaAllocationStrategy(),
+ new InterleavedNumaAllocationStrategy(IntStream.range(0,
NumaAllocUtil.NUMA_NODES_CNT).toArray()),
+ new SimpleNumaAllocationStrategy(),
+ new SimpleNumaAllocationStrategy(NumaAllocUtil.NUMA_NODES_CNT - 1)
+ )
+ .flatMap(strategy -> Stream.of(new Object[]{strategy, true}, new
Object[]{strategy, false}))
+ .collect(Collectors.toList());
+ }
+
+ /** */
+ @Parameterized.Parameter(0)
+ public NumaAllocationStrategy strategy;
+
+ /** */
+ @Parameterized.Parameter(1)
+ public boolean defaultConfig;
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String
igniteInstanceName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+ DataStorageConfiguration memCfg = new DataStorageConfiguration();
+
+ DataRegionConfiguration dfltReg = new DataRegionConfiguration()
+ .setInitialSize(INITIAL_SIZE)
+ .setMaxSize(MAX_SIZE)
+ .setMetricsEnabled(true);
+
+ NumaAllocator memAlloc = new NumaAllocator(strategy);
+
+ if (defaultConfig)
+ memCfg.setMemoryAllocator(memAlloc);
+ else
+ dfltReg.setMemoryAllocator(memAlloc);
+
+ memCfg.setDefaultDataRegionConfiguration(dfltReg);
+
+ cfg.setDataStorageConfiguration(memCfg);
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ startGrids(NUM_NODES);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ super.afterTest();
+
+ stopAllGrids(true);
+ }
+
+ /** */
+ @Test
+ public void testLoadData() throws Exception {
+ IgniteEx client = startClientGrid("client");
+
+ client.getOrCreateCache(TEST_CACHE);
+
+ try (IgniteDataStreamer<Integer, byte[]> ds =
client.dataStreamer(TEST_CACHE)) {
+ int cnt = 0;
+ while (hasFreeSpace()) {
+ ds.addData(++cnt, BUF);
+
+ if (cnt % 100 == 0)
+ ds.flush();
+ }
+ }
+
+ assertEquals(NUM_NODES, serverGrids().count());
+
+ serverGrids().forEach(g -> {
+ assertTrue(getDefaultRegion(g).config().getMemoryAllocator()
instanceof NumaAllocator);
+ });
+ }
+
+ /** */
+ private boolean hasFreeSpace() {
+ return serverGrids().allMatch(g -> {
+ DataRegion dr = getDefaultRegion(g);
+
+ return dr.metrics().getTotalAllocatedSize() < 0.9 * MAX_SIZE;
+ });
+ }
+
+ /** */
+ private static Stream<IgniteEx> serverGrids() {
+ return G.allGrids().stream().filter(g ->
!g.cluster().localNode().isClient()).map(g -> (IgniteEx)g);
+ }
+
+ /** */
+ private static DataRegion getDefaultRegion(IgniteEx g) {
+ assertFalse(g.cluster().localNode().isClient());
+
+ String dataRegionName = g.configuration().getDataStorageConfiguration()
+ .getDefaultDataRegionConfiguration().getName();
+
+ try {
+ return
g.context().cache().context().database().dataRegion(dataRegionName);
+ }
+ catch (IgniteCheckedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git
a/modules/numa-allocator/src/test/java/org/apache/ignite/internal/mem/NumaAllocatorUnitTest.java
b/modules/numa-allocator/src/test/java/org/apache/ignite/internal/mem/NumaAllocatorUnitTest.java
new file mode 100644
index 0000000..7044ea8
--- /dev/null
+++
b/modules/numa-allocator/src/test/java/org/apache/ignite/internal/mem/NumaAllocatorUnitTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.mem;
+
+import java.util.Arrays;
+import java.util.stream.IntStream;
+import org.apache.ignite.internal.util.GridUnsafe;
+import org.apache.ignite.mem.InterleavedNumaAllocationStrategy;
+import org.apache.ignite.mem.LocalNumaAllocationStrategy;
+import org.apache.ignite.mem.NumaAllocationStrategy;
+import org.apache.ignite.mem.NumaAllocator;
+import org.apache.ignite.mem.SimpleNumaAllocationStrategy;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** */
+@RunWith(Enclosed.class)
+public class NumaAllocatorUnitTest {
+ /** */
+ @RunWith(Parameterized.class)
+ public static class PositiveScenarioTest extends GridCommonAbstractTest {
+ /** */
+ private static final long BUF_SZ = 32 * 1024 * 1024;
+
+ /** */
+ private static final int[] EVEN_NODES = IntStream.range(0,
NumaAllocUtil.NUMA_NODES_CNT)
+ .filter(x -> x % 2 == 0).toArray();
+
+ /** */
+ private static final int[] ALL_NODES = IntStream.range(0,
NumaAllocUtil.NUMA_NODES_CNT).toArray();
+
+ /**
+ *
+ */
+ @Parameterized.Parameters(name = "allocationStrategy={0}")
+ public static Iterable<Object[]> data() {
+ return Arrays.asList(
+ new Object[] {new LocalNumaAllocationStrategy()},
+ new Object[] {new InterleavedNumaAllocationStrategy()},
+ new Object[] {new InterleavedNumaAllocationStrategy(new
int[0])},
+ new Object[] {new
InterleavedNumaAllocationStrategy(EVEN_NODES)},
+ new Object[] {new
InterleavedNumaAllocationStrategy(ALL_NODES)},
+ new Object[] {new SimpleNumaAllocationStrategy()},
+ new Object[] {new
SimpleNumaAllocationStrategy(NumaAllocUtil.NUMA_NODES_CNT - 1)}
+ );
+ }
+
+ /** */
+ @Parameterized.Parameter()
+ public NumaAllocationStrategy strategy;
+
+ /** */
+ @Test
+ public void test() {
+ NumaAllocator allocator = new NumaAllocator(strategy);
+
+ long ptr = 0;
+ try {
+ ptr = allocator.allocateMemory(BUF_SZ);
+
+ assertEquals(BUF_SZ, NumaAllocUtil.chunkSize(ptr));
+
+ GridUnsafe.setMemory(ptr, BUF_SZ, (byte)1);
+
+ for (long i = 0; i < BUF_SZ; i++)
+ assertEquals((byte)1, GridUnsafe.getByte(ptr + i));
+ }
+ finally {
+ if (ptr != 0)
+ allocator.freeMemory(ptr);
+ }
+ }
+ }
+
+ /** */
+ public static class ErrorScenarioTest extends GridCommonAbstractTest {
+ /** */
+ @Test
+ public void testInvalidInterleavedStrategyParams() {
+ int[][] invalidNodes = {
+ {-3, -4, 0},
+ IntStream.range(0, NumaAllocUtil.NUMA_NODES_CNT + 1).toArray()
+ };
+
+ for (int[] nodeSet: invalidNodes) {
+ GridTestUtils.assertThrows(log(), () -> new
InterleavedNumaAllocationStrategy(nodeSet),
+ IllegalArgumentException.class, null);
+ }
+ }
+
+ /** */
+ @Test
+ public void testInvalidSimpleStrategyParams() {
+ int[] invalidNodes = {-3, NumaAllocUtil.NUMA_NODES_CNT};
+
+ for (int node: invalidNodes) {
+ GridTestUtils.assertThrows(log(), () -> new
SimpleNumaAllocationStrategy(node),
+ IllegalArgumentException.class, null);
+ }
+ }
+ }
+}
diff --git
a/modules/numa-allocator/src/test/java/org/apache/ignite/testsuites/NumaAllocatorTestSuite.java
b/modules/numa-allocator/src/test/java/org/apache/ignite/testsuites/NumaAllocatorTestSuite.java
new file mode 100644
index 0000000..9fc6ab6
--- /dev/null
+++
b/modules/numa-allocator/src/test/java/org/apache/ignite/testsuites/NumaAllocatorTestSuite.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.testsuites;
+
+import org.apache.ignite.internal.mem.NumaAllocatorBasicTest;
+import org.apache.ignite.internal.mem.NumaAllocatorUnitTest;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/** */
+@RunWith(Suite.class)
[email protected]({
+ NumaAllocatorUnitTest.class,
+ NumaAllocatorBasicTest.class
+})
+public class NumaAllocatorTestSuite {
+}
diff --git a/parent/pom.xml b/parent/pom.xml
index a57e612..38e1dfb 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -366,6 +366,10 @@
<packages>org.apache.ignite.mbean:org.apache.ignite.mxbean</packages>
</group>
<group>
+ <title>Memory allocator APIs</title>
+ <packages>org.apache.ignite.mem</packages>
+ </group>
+ <group>
<title>SPI: CheckPoint</title>
<packages>org.apache.ignite.spi.checkpoint*</packages>
</group>
diff --git a/pom.xml b/pom.xml
index f93a4c3..715cf1c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -99,6 +99,7 @@
<module>modules/hibernate-4.2</module>
<module>modules/hibernate-5.1</module>
<module>modules/hibernate-5.3</module>
+ <module>modules/numa-allocator</module>
<module>modules/schedule</module>
<module>modules/yardstick</module>
</modules>
@@ -131,6 +132,13 @@
</profile>
<profile>
+ <id>numa-allocator</id>
+ <modules>
+ <module>modules/numa-allocator</module>
+ </modules>
+ </profile>
+
+ <profile>
<id>test</id>
<build>
<plugins>