Commit 23b69254 authored by Swann Perarnau's avatar Swann Perarnau
Browse files

Merge branch 'doc_tutorial_dma' into 'staging'

[doc] area tutorial

See merge request !118
parents 157d8915 1e517e34
Pipeline #9642 passed with stages
in 3 minutes and 17 seconds
......@@ -17,9 +17,7 @@ HELLO_TESTS = \
hello_world/0_hello
AREA_TESTS = \
area/0_isInterleaved \
area/1_aml_area_linux \
area/2_custom_interleave_area
area/1_custom_interleave_area
LAYOUT_TESTS = \
layouts/0_dense_layout \
......@@ -35,7 +33,7 @@ TILING_TESTS = \
tiling/1_dgemm
if HAVE_CUDA
AREA_TESTS += area/3_aml_area_cuda
AREA_TESTS += area/0_aml_area_cuda
endif
UNIT_TESTS = $(HELLO_TESTS) $(AREA_TESTS) $(LAYOUT_TESTS) $(DMA_TESTS) $(TILING_TESTS)
......
/*******************************************************************************
* Copyright 2019 UChicago Argonne, LLC.
* (c.f. AUTHORS, LICENSE)
*
* This file is part of the AML project.
* For more info, see https://xgitlab.cels.anl.gov/argo/aml
*
* SPDX-License-Identifier: BSD-3-Clause
******************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <aml.h>
#include <aml/area/linux.h>
#include "tutorials.h"
void test_default_area(const size_t size)
{
void *buf;
buf = aml_area_mmap(&aml_area_linux, size, NULL);
if (buf == NULL) {
aml_perror("aml_area_linux");
exit(1);
}
printf("Default linux area worked!\n");
aml_area_munmap(&aml_area_linux, buf, size);
}
void test_interleave_area(const size_t size)
{
int err;
void *buf;
struct aml_area *interleave_area;
// Create interleave area on all nodes.
err = aml_area_linux_create(&interleave_area,
NULL, AML_AREA_LINUX_POLICY_INTERLEAVE);
if (err != AML_SUCCESS) {
fprintf(stderr, "aml_area_linux_create: %s\n",
aml_strerror(err));
exit(1);
}
// Map buffer in area.
buf = aml_area_mmap(interleave_area, size, NULL);
if (buf == NULL) {
aml_perror("aml_area_linux");
exit(1);
}
// Check it is indeed interleaved
if (!is_interleaved(buf, size))
exit(1);
printf("Interleave linux area worked and is interleaved.\n");
// Cleanup
aml_area_munmap(interleave_area, buf, size);
aml_area_linux_destroy(&interleave_area);
}
int main(void)
{
const size_t size = (2 << 16); // 16 pages
test_default_area(size);
test_interleave_area(size);
return 0;
}
......@@ -9,45 +9,23 @@
******************************************************************************/
#define _GNU_SOURCE
#include <aml.h>
#include <numa.h>
#include <numaif.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h> // sysconf()
#include <sys/mman.h> // mmap()
#include <numa.h> // #define NUMA_NUM_NODES
#include <numaif.h> // get_mempolicy()
/**
* Check whether linux inerleave binding policy
* is set on this range of address.
**/
int is_interleaved_policy(void *data)
{
long err;
int policy;
unsigned long maxnode = sizeof(unsigned long) * 8;
unsigned long nmask;
err = get_mempolicy(&policy, &nmask, maxnode, data, MPOL_F_ADDR);
if (err == -1) {
perror("get_mempolicy");
exit(1);
}
return policy == MPOL_INTERLEAVE;
}
#include <stdlib.h>
#include <unistd.h>
/**
* Returns the numa node on which data is bound
* or -1 if there is more than one node involved in binding.
**/
int get_node(void *data)
// Function to get last NUMA node id on which data is allocated.
static int
get_node(void *data)
{
long err;
int policy;
unsigned long maxnode = sizeof(unsigned long) * 8;
unsigned long nmask = 0;
int node = -1;
unsigned long nmask = 0;
int node = -1;
err = get_mempolicy(&policy, &nmask, maxnode, data, MPOL_F_ADDR);
if (err == -1) {
......@@ -63,26 +41,23 @@ int get_node(void *data)
return node;
}
/**
* Walk all pages and check if they are bound in a round-robin
* fashion.
**/
int is_interleaved_bind(void *data, const size_t size)
// Check if data of size `size` is interleaved on all nodes,
// by chunk of size `page_size`.
static int
is_interleaved(void *data, const size_t size, const size_t page_size)
{
int page_size;
intptr_t start;
int node, next, num_nodes = 0;
page_size = sysconf(_SC_PAGESIZE);
start = ((intptr_t) data) << page_size >> page_size;
start = ((intptr_t)data) << page_size >> page_size;
node = get_node((void *)start);
// more than one node in policy.
if (node < 0)
return 0;
for (intptr_t page = start + page_size;
(size_t) (page - start) < size; page += page_size) {
for (intptr_t page = start + page_size; (size_t)(page - start) < size;
page += page_size) {
next = get_node((void *)page);
// more than one node in page policy.
......@@ -106,92 +81,118 @@ int is_interleaved_bind(void *data, const size_t size)
return 1;
}
/**
* data is interleaved if the interleave binding policy is set
* or if pages are bound in a round-robin fashion.
**/
int is_interleaved(void *data, const size_t size)
// Custom area attributes.
struct area_data {
unsigned long nid; // Current node id;
unsigned long nmax; // maximum amount of numa nodes in this system.
int page_size; // Size of a page;
};
// Custom area mmap implementation
void *
custom_mmap(const struct aml_area_data *data,
size_t size,
struct aml_area_mmap_options *opts)
{
return is_interleaved_policy(data) || is_interleaved_bind(data, size);
}
void *mmap_interleave_policy(const size_t size)
{
const unsigned long NUMA_NODES[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
void *out = mmap(NULL, size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
0, 0);
if (out == NULL) {
perror("mmap");
exit(1);
(void)opts;
intptr_t start;
struct area_data *area = (struct area_data *)data;
void *ret = mmap(NULL,
size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
0,
0);
if (ret == NULL)
return NULL;
start = (intptr_t)ret >> area->page_size << area->page_size;
for (intptr_t page = start; page < (start + (intptr_t)size);
page += area->page_size) {
if (mbind((void *)page,
area->page_size,
MPOL_BIND,
&area->nid,
8 * sizeof(unsigned long),
MPOL_MF_MOVE) == -1) {
perror("mbind");
munmap(ret, size);
return NULL;
}
area->nid = area->nid << 1;
if (area->nid >= (1UL << area->nmax))
area->nid = 1;
}
if (mbind(out, size, MPOL_INTERLEAVE, NUMA_NODES,
sizeof(NUMA_NODES) / sizeof(*NUMA_NODES),
MPOL_MF_MOVE) == -1) {
perror("mbind");
munmap(out, size);
exit(1);
}
return ret;
}
return out;
// Custom area munmap implementation
int
custom_munmap(const struct aml_area_data *data, void *ptr, size_t size)
{
(void)data;
munmap(ptr, size);
return AML_SUCCESS;
}
void *mmap_interleave_bind(const size_t size)
// Custom area constructor
struct aml_area *
custom_area_create()
{
const int numnode = numa_max_node() == 0 ? 1 : numa_max_node();
const unsigned long maxnode = sizeof(unsigned long) * 8;
unsigned long NUMA_NODES = 1;
int page_size = sysconf(_SC_PAGESIZE);
intptr_t start;
int node = 0;
void *out;
struct area_data *data;
struct aml_area_ops *ops;
struct aml_area *ret = AML_INNER_MALLOC(
struct aml_area, struct aml_area_ops, struct area_data);
if (ret == NULL)
return NULL;
ret->ops = AML_INNER_MALLOC_GET_FIELD(
ret, 2, struct aml_area, struct aml_area_ops, struct area_data);
ops = ret->ops;
ops->mmap = custom_mmap;
ops->munmap = custom_munmap;
ret->data = AML_INNER_MALLOC_GET_FIELD(
ret, 3, struct aml_area, struct aml_area_ops, struct area_data);
data = (struct area_data *)ret->data;
data->nid = 1;
data->nmax = numa_max_node() == 0 ? 1 : numa_max_node();
data->page_size = 2 * sysconf(_SC_PAGESIZE); // 2 pages allocation
return ret;
}
out = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
// Test that custom area will interleave pages as expected.
void
test_custom_area(const size_t size)
{
void *buf;
struct aml_area *interleave_area = custom_area_create();
if (out == NULL) {
perror("mmap");
// Map buffer in area.
buf = aml_area_mmap(interleave_area, size, NULL);
if (buf == NULL) {
aml_perror("aml_area_linux");
exit(1);
}
// Check it is indeed interleaved
if (!is_interleaved(buf, size, 2 * sysconf(_SC_PAGESIZE)))
exit(1);
printf("Custom area worked and is interleaved.\n");
start = ((intptr_t) out) << page_size >> page_size;
for (intptr_t page = start; (size_t) (page - start) < size;
page += page_size) {
NUMA_NODES = 1 << node;
if (mbind((void *)page, page_size, MPOL_BIND, &NUMA_NODES,
maxnode, MPOL_MF_MOVE) == -1) {
perror("mbind");
munmap(out, size);
exit(1);
}
node = (node + 1) % numnode;
}
return out;
// Cleanup
aml_area_munmap(interleave_area, buf, size);
free(interleave_area);
}
int main(void)
int
main(void)
{
void *buf;
const size_t size = (2 << 16); // 16 pages
// Check that linux bind interleave policy passes test.
buf = mmap_interleave_policy(size);
if (!is_interleaved(buf, size))
return 1;
printf("mmap_interleave_policy check works!\n");
munmap(buf, size);
// Check that binding pages in a round-robin fashion passes test.
buf = mmap_interleave_bind(size);
if (!is_interleaved(buf, size))
return 1;
printf("mmap_interleave_bind check works!\n");
munmap(buf, size);
const size_t size = (2 << 16); // 16 pages
test_custom_area(size);
return 0;
}
/*******************************************************************************
* Copyright 2019 UChicago Argonne, LLC.
* (c.f. AUTHORS, LICENSE)
*
* This file is part of the AML project.
* For more info, see https://xgitlab.cels.anl.gov/argo/aml
*
* SPDX-License-Identifier: BSD-3-Clause
******************************************************************************/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <aml.h>
#include "tutorials.h"
struct area_data {
unsigned long nid; // Current node id;
unsigned long nmax; // maximum amount of numa nodes in this system.
int page_size; // Size of a page;
};
void *custom_mmap(const struct aml_area_data *data,
size_t size, struct aml_area_mmap_options *opts)
{
(void)opts;
intptr_t start;
struct area_data *area = (struct area_data *)data;
void *ret = mmap(NULL, size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
0, 0);
if (ret == NULL)
return NULL;
start = (intptr_t) ret >> area->page_size << area->page_size;
for (intptr_t page = start; page < (start + (intptr_t) size);
page += area->page_size) {
if (mbind
((void *)page, area->page_size, MPOL_BIND, &area->nid,
8 * sizeof(unsigned long), MPOL_MF_MOVE) == -1) {
perror("mbind");
munmap(ret, size);
return NULL;
}
area->nid = area->nid << 1;
if (area->nid >= (1UL << area->nmax))
area->nid = 1;
}
return ret;
}
int custom_munmap(const struct aml_area_data *data, void *ptr, size_t size)
{
(void)data;
munmap(ptr, size);
return AML_SUCCESS;
}
struct aml_area *custom_area_create()
{
struct area_data *data;
struct aml_area_ops *ops;
struct aml_area *ret = AML_INNER_MALLOC(struct aml_area,
struct aml_area_ops,
struct area_data);
if (ret == NULL)
return NULL;
ret->ops = AML_INNER_MALLOC_GET_FIELD(ret, 2,
struct aml_area,
struct aml_area_ops,
struct area_data);
ops = ret->ops;
ops->mmap = custom_mmap;
ops->munmap = custom_munmap;
ret->data = AML_INNER_MALLOC_GET_FIELD(ret, 3,
struct aml_area,
struct aml_area_ops,
struct area_data);
data = (struct area_data *)ret->data;
data->nid = 1;
data->nmax = numa_max_node() == 0 ? 1 : numa_max_node();
data->page_size = sysconf(_SC_PAGESIZE);
return ret;
}
void test_custom_area(const size_t size)
{
void *buf;
struct aml_area *interleave_area = custom_area_create();
// Map buffer in area.
buf = aml_area_mmap(interleave_area, size, NULL);
if (buf == NULL) {
aml_perror("aml_area_linux");
exit(1);
}
// Check it is indeed interleaved
if (!is_interleaved(buf, size))
exit(1);
printf("Custom area worked and is interleaved.\n");
// Cleanup
aml_area_munmap(interleave_area, buf, size);
free(interleave_area);
}
int main(void)
{
const size_t size = (2 << 16); // 16 pages
test_custom_area(size);
return 0;
}
# AML interleave tutorials
This tutorial aims at learn how to use and implement AML areas.
Through this exercise, you will:
* Use existing AML area to allocate data on several memories
in an interleave fashion.
* Implement a custom AML area that you will further
use to allocate data in an interleave fashion.
* Use existing AML area to allocate data on CUDA device.
# Tutorials
## [0_isInterleaved](./0_isInterleaved.c)
This tutorial set up test functions to check weather
a buffer is allocated in a round robin fashion.
The check looks if the memory policy associated with
the buffer is MPOL_INTERLEAVE, or if every pages in
the buffer are allocated on the machine nodes in a
round-robin fashion.
We further allocate buffers in such manners and
check that the tests are reporting the good result.
The result of this exercise is then put in the file
[tutorials.h](./tutorials.h) to check the next exercises.
### Exercise
```
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h> // sysconf()
#include <sys/mman.h> // mmap()
#include <numa.h> // numa_get_max_node()
#include <numaif.h> // get_mempolicy()
/**
* Check whether linux inerleave binding policy
* is set on this range of address.
**/
int is_interleaved_policy(void *data)
{
// Exercise
}
/**
* Walk all pages and check if they are bound in a round-robin
* fashion.
**/
int is_interleaved_bind(void *data, const size_t size)
{
// Exercise
}
/**
* data is interleaved if the interleave binding policy is set
* or if pages are bound in a round-robin fashion.
**/
int is_interleaved(void *data, const size_t size)
{
return is_interleaved_policy(data) || is_interleaved_bind(data, size);
}
void *mmap_interleave_policy(const size_t size)
{
const unsigned long NUMA_NODES[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
void *out = mmap(NULL, size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
0, 0);
if (out == NULL) {
perror("mmap");
exit(1);
}
if (mbind(out, size, MPOL_INTERLEAVE, NUMA_NODES,
sizeof(NUMA_NODES) / sizeof(*NUMA_NODES),
MPOL_MF_MOVE) == -1) {
perror("mbind");
munmap(out, size);
exit(1);
}
return out;
}
void *mmap_interleave_bind(const size_t size)
{
const int numnode = numa_max_node() == 0 ? 1 : numa_max_node();
const unsigned long maxnode = sizeof(unsigned long) * 8;
unsigned long NUMA_NODES = 1;
int page_size = sysconf(_SC_PAGESIZE);
intptr_t start;
int node = 0;
void *out;
out = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (out == NULL) {
perror("mmap");
exit(1);
}
start = ((intptr_t) out) << page_size >> page_size;
for (intptr_t page = start; (size_t) (page - start) < size;
page += page_size) {
NUMA_NODES = 1 << node;
if (mbind((void *)page, page_size, MPOL_BIND, &NUMA_NODES,
maxnode, MPOL_MF_MOVE) == -1) {
perror("mbind");
munmap(out, size);
exit(1);
}
node = (node + 1) % numnode;
}