Skip to content

Instantly share code, notes, and snippets.

@bvisness
Last active August 4, 2023 02:08
Show Gist options
  • Save bvisness/b29fe28048645c49fdb4d4e86b7018a9 to your computer and use it in GitHub Desktop.
Save bvisness/b29fe28048645c49fdb4d4e86b7018a9 to your computer and use it in GitHub Desktop.
memory.discard test programs
// Run like so:
// gcc -opageroni pageroni_linux.c && pageroni
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM_PAGES 1000000
#define NUM_CHUNKS 4
#define PAGES_PER_CHUNK (NUM_PAGES / NUM_CHUNKS)
// Change these to try different test cases
#define DO_MADVISE 0 // use madvise(MADV_DONTNEED) instead of mmap(MAP_FIXED)
#define MMAP_MODE MAP_PRIVATE // or MAP_SHARED
#define MULTIPLE_PROCESSES 0
void done(int *pfds);
void waitDone(int *pfds);
int main() {
size_t pageSize = sysconf(_SC_PAGESIZE);
printf("The page size for this system is %ld bytes.\n", pageSize);
printf("Getting %lu pages of memory\n", (size_t)NUM_PAGES);
char *mem = mmap(NULL, pageSize * (size_t)NUM_PAGES, PROT_READ|PROT_WRITE, MAP_ANON|MMAP_MODE, -1, 0);
if (mem == MAP_FAILED) {
printf("mmap failed: %s\n", strerror(errno));
return 1;
}
sleep(3);
printf("Touching each page to allocate resources\n");
for (size_t i = 0; i < NUM_CHUNKS; i++) {
for (size_t j = 0; j < PAGES_PER_CHUNK; j++) {
mem[i*PAGES_PER_CHUNK*pageSize + j*pageSize] = (char)j;
}
printf("Touched %lu of %d chunks\n", i+1, NUM_CHUNKS);
sleep(3);
}
char *chunk2 = mem + (size_t)PAGES_PER_CHUNK*pageSize;
char *chunk3 = mem + (size_t)PAGES_PER_CHUNK*pageSize*2;
int p2c[2];
int c2p[2];
int childPid = 123;
if (MULTIPLE_PROCESSES) {
pipe(p2c);
pipe(c2p);
childPid = fork();
}
if (childPid == 0) {
// am child
// wait for parent to read from second chunk
waitDone(p2c);
printf("CHILD: Reading, not writing, from the third chunk\n");
int sum = 0;
for (size_t i = 0; i < (size_t)PAGES_PER_CHUNK*pageSize; i++) {
sum += chunk3[i];
}
printf("CHILD: The sum of the data is %d\n", sum);
if (sum == 0) {
printf("CHILD: The pages were zeroed, as expected.\n");
} else {
printf("CHILD: The pages were NOT zeroed! This strategy does not work!\n");
}
sleep(5);
// wait for parent to touch second chunk
done(c2p);
waitDone(p2c);
printf("CHILD: Touching everything in the third chunk again\n");
for (size_t i = 0; i < PAGES_PER_CHUNK; i++) {
chunk3[i*pageSize] = (char)(-i);
}
printf("CHILD: The third chunk has now been touched.\n");
sleep(10);
printf("CHILD: I die\n");
done(c2p);
return 0;
} else {
// am parent
//
// DISCARD THE SECOND CHUNK
//
if (DO_MADVISE) {
printf("PARENT: MADV_DONTNEED-ing the second chunk\n");
madvise(chunk2, (size_t)PAGES_PER_CHUNK*pageSize, MADV_DONTNEED);
sleep(5);
} else {
printf("PARENT: Remapping the second chunk\n");
mmap(chunk2, (size_t)PAGES_PER_CHUNK*pageSize, PROT_READ|PROT_WRITE, MAP_ANON|MMAP_MODE|MAP_FIXED, -1, 0);
sleep(5);
}
if (MULTIPLE_PROCESSES) {
//
// DISCARD THE THIRD CHUNK
//
if (DO_MADVISE) {
printf("PARENT: MADV_DONTNEED-ing the third chunk\n");
madvise(chunk3, (size_t)PAGES_PER_CHUNK*pageSize, MADV_DONTNEED);
sleep(5);
} else {
printf("PARENT: Remapping the third chunk\n");
mmap(chunk3, (size_t)PAGES_PER_CHUNK*pageSize, PROT_READ|PROT_WRITE, MAP_ANON|MMAP_MODE|MAP_FIXED, -1, 0);
sleep(5);
}
}
//
// READ FROM THE SECOND CHUNK
//
printf("PARENT: Reading, not writing, from the second chunk\n");
int sum = 0;
for (size_t i = 0; i < (size_t)PAGES_PER_CHUNK*pageSize; i++) {
sum += chunk2[i];
}
printf("PARENT: The sum of the data is %d\n", sum);
if (sum == 0) {
printf("PARENT: The pages were zeroed, as expected.\n");
} else {
printf("PARENT: The pages were NOT zeroed! This strategy does not work!\n");
}
sleep(5);
if (MULTIPLE_PROCESSES) {
// wait for child to read from third chunk
done(p2c);
waitDone(c2p);
}
//
// TOUCH THE SECOND CHUNK
//
printf("PARENT: Touching everything in the second chunk again\n");
for (size_t i = 0; i < PAGES_PER_CHUNK; i++) {
chunk2[i*pageSize] = (char)(-i);
}
printf("PARENT: The second chunk has now been touched.\n");
sleep(5);
if (MULTIPLE_PROCESSES) {
// wait for child to touch third chunk
done(p2c);
waitDone(c2p);
}
printf("PARENT: sleepy time\n");
sleep(60);
if (MULTIPLE_PROCESSES) {
printf("PARENT: Waiting for child to finish, just in case...\n");
waitpid(childPid, NULL, 0);
}
printf("PARENT: I die\n");
return 0;
}
}
void done(int *pfds) {
char _ = 1;
write(pfds[1], &_, 1);
}
void waitDone(int *pfds) {
char _ = 0;
read(pfds[0], &_, 1);
}
// Run like so:
// zig run pageroni_mac.c
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM_PAGES 1000000
#define NUM_CHUNKS 4
#define PAGES_PER_CHUNK (NUM_PAGES / NUM_CHUNKS)
// Change these to try different test cases.
// Strategies:
// 0 = madvise(MADV_DONTNEED)
// 1 = madvise(MADV_FREE_REUSABLE)
// 2 = madvise(MADV_FREE_REUSABLE); madvise(MADV_FREE_REUSE)
// 3 = mmap(MAP_FIXED)
#define STRATEGY 3
#define MMAP_MODE MAP_SHARED // or MAP_SHARED
#define MULTIPLE_PROCESSES 1
void done(int *pfds);
void waitDone(int *pfds);
int main() {
size_t pageSize = sysconf(_SC_PAGESIZE);
printf("The page size for this system is %ld bytes.\n", pageSize);
printf("Getting %lu pages of memory\n", (size_t)NUM_PAGES);
char *mem = mmap(NULL, pageSize * (size_t)NUM_PAGES, PROT_READ|PROT_WRITE, MAP_ANON|MMAP_MODE, -1, 0);
if (mem == MAP_FAILED) {
printf("mmap failed: %s\n", strerror(errno));
return 1;
}
sleep(3);
printf("Touching each page to allocate resources\n");
for (size_t i = 0; i < NUM_CHUNKS; i++) {
for (size_t j = 0; j < PAGES_PER_CHUNK; j++) {
mem[i*PAGES_PER_CHUNK*pageSize + j*pageSize] = (char)j;
}
printf("Touched %lu of %d chunks\n", i+1, NUM_CHUNKS);
sleep(3);
}
char *chunk2 = mem + (size_t)PAGES_PER_CHUNK*pageSize;
char *chunk3 = mem + (size_t)PAGES_PER_CHUNK*pageSize*2;
int p2c[2];
int c2p[2];
int childPid = 123;
if (MULTIPLE_PROCESSES) {
pipe(p2c);
pipe(c2p);
childPid = fork();
}
if (childPid == 0) {
// am child
// wait for parent to read from second chunk
waitDone(p2c);
printf("CHILD: Reading, not writing, from the third chunk\n");
int sum = 0;
for (size_t i = 0; i < (size_t)PAGES_PER_CHUNK*pageSize; i++) {
sum += chunk3[i];
}
printf("CHILD: The sum of the data is %d\n", sum);
if (sum == 0) {
printf("CHILD: The pages were zeroed, as expected.\n");
} else {
printf("CHILD: The pages were NOT zeroed! This strategy does not work!\n");
}
sleep(5);
// wait for parent to touch second chunk
done(c2p);
waitDone(p2c);
printf("CHILD: Touching everything in the third chunk again\n");
if (STRATEGY == 2) {
printf("CHILD: MADV_FREE_REUSE-ing the third chunk\n");
madvise(chunk3, (size_t)PAGES_PER_CHUNK*pageSize, MADV_FREE_REUSE);
sleep(5);
}
for (size_t i = 0; i < PAGES_PER_CHUNK; i++) {
chunk3[i*pageSize] = (char)(-i);
}
printf("CHILD: The third chunk has now been touched.\n");
sleep(10);
printf("CHILD: I die\n");
done(c2p);
return 0;
} else {
// am parent
//
// DISCARD THE SECOND CHUNK
//
switch (STRATEGY) {
case 0: {
printf("PARENT: MADV_DONTNEED-ing the second chunk\n");
madvise(chunk2, (size_t)PAGES_PER_CHUNK*pageSize, MADV_DONTNEED);
sleep(5);
} break;
case 1:
case 2: {
printf("PARENT: MADV_FREE_REUSABLE-ing the second chunk\n");
madvise(chunk2, (size_t)PAGES_PER_CHUNK*pageSize, MADV_FREE_REUSABLE);
sleep(5);
} break;
case 3: {
printf("PARENT: Remapping the second chunk\n");
mmap(chunk2, (size_t)PAGES_PER_CHUNK*pageSize, PROT_READ|PROT_WRITE, MAP_ANON|MMAP_MODE|MAP_FIXED, -1, 0);
sleep(5);
} break;
}
if (MULTIPLE_PROCESSES) {
//
// DISCARD THE THIRD CHUNK
//
switch (STRATEGY) {
case 0: {
printf("PARENT: MADV_DONTNEED-ing the third chunk\n");
madvise(chunk3, (size_t)PAGES_PER_CHUNK*pageSize, MADV_DONTNEED);
sleep(5);
} break;
case 1:
case 2: {
printf("PARENT: MADV_FREE_REUSABLE-ing the third chunk\n");
madvise(chunk3, (size_t)PAGES_PER_CHUNK*pageSize, MADV_FREE_REUSABLE);
sleep(5);
} break;
case 3: {
printf("PARENT: Remapping the third chunk\n");
mmap(chunk3, (size_t)PAGES_PER_CHUNK*pageSize, PROT_READ|PROT_WRITE, MAP_ANON|MMAP_MODE|MAP_FIXED, -1, 0);
sleep(5);
} break;
}
}
//
// READ FROM THE SECOND CHUNK
//
printf("PARENT: Reading, not writing, from the second chunk\n");
int sum = 0;
for (size_t i = 0; i < (size_t)PAGES_PER_CHUNK*pageSize; i++) {
sum += chunk2[i];
}
printf("PARENT: The sum of the data is %d\n", sum);
if (sum == 0) {
printf("PARENT: The pages were zeroed, as expected.\n");
} else {
printf("PARENT: The pages were NOT zeroed! This strategy does not work!\n");
}
sleep(5);
if (MULTIPLE_PROCESSES) {
// wait for child to read from third chunk
done(p2c);
waitDone(c2p);
}
//
// TOUCH THE SECOND CHUNK
//
printf("PARENT: Touching everything in the second chunk again\n");
if (STRATEGY == 2) {
printf("PARENT: MADV_FREE_REUSE-ing the second chunk\n");
madvise(chunk2, (size_t)PAGES_PER_CHUNK*pageSize, MADV_FREE_REUSE);
sleep(5);
}
for (size_t i = 0; i < PAGES_PER_CHUNK; i++) {
chunk2[i*pageSize] = (char)(-i);
}
printf("PARENT: The second chunk has now been touched.\n");
sleep(5);
if (MULTIPLE_PROCESSES) {
// wait for child to touch third chunk
done(p2c);
waitDone(c2p);
}
printf("PARENT: sleepy time\n");
sleep(60);
if (MULTIPLE_PROCESSES) {
printf("PARENT: Waiting for child to finish, just in case...\n");
waitpid(childPid, NULL, 0);
}
printf("PARENT: I die\n");
return 0;
}
}
void done(int *pfds) {
char _ = 1;
write(pfds[1], &_, 1);
}
void waitDone(int *pfds) {
char _ = 0;
read(pfds[0], &_, 1);
}
// Run like so:
// clang -opageroni.exe pageroni_win.c && pageroni.exe
#include <stdio.h>
#include <windows.h>
#include <memoryapi.h>
#define NUM_PAGES 1000000
#define NUM_CHUNKS 4
#define PAGES_PER_CHUNK (NUM_PAGES / NUM_CHUNKS)
// Change these to try different test cases.
// For more information about these strategies, see
// https://devblogs.microsoft.com/oldnewthing/20170113-00/?p=95185
//
// Strategies:
// 0 = VirtualAlloc(MEM_RESET)
// 1 = VirtualAlloc(MEM_COMMIT) (on existing mapping)
// 2 = VirtualFree(MEM_DECOMMIT); VirtualAlloc(MEM_COMMIT)
// 3 = ZeroMemory()
// 4 = VirtualUnlock()
// 5 = DiscardVirtualMemory()
#define STRATEGY 5
int main() {
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
size_t pageSize = systemInfo.dwPageSize;
printf("The page size for this system is %zu bytes.\n", pageSize);
Sleep(5000);
printf("Getting %zu pages of memory\n", (size_t)NUM_PAGES);
char *mem = VirtualAlloc(NULL, pageSize * (size_t)NUM_PAGES, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
if (!mem) {
printf("VirtualAlloc failed: %lu\n", GetLastError());
return 1;
}
Sleep(3000);
printf("Touching each page to allocate resources\n");
for (size_t i = 0; i < NUM_CHUNKS; i++) {
for (size_t j = 0; j < PAGES_PER_CHUNK; j++) {
mem[i*PAGES_PER_CHUNK*pageSize + j*pageSize] = (char)j;
}
printf("Touched %llu of %d chunks\n", i+1, NUM_CHUNKS);
Sleep(3000);
}
char *chunk2 = mem + (size_t)PAGES_PER_CHUNK*pageSize;
char *chunk3 = mem + (size_t)PAGES_PER_CHUNK*pageSize*2;
//
// DISCARD THE SECOND CHUNK
//
switch (STRATEGY) {
case 0: {
printf("MEM_RESET-ing the second chunk\n");
if (!VirtualAlloc(chunk2, (size_t)PAGES_PER_CHUNK*pageSize, MEM_RESET, PAGE_NOACCESS)) {
printf("Failed to MEM_RESET!\n");
}
Sleep(5000);
} break;
case 1:
case 2: {
if (STRATEGY == 2) {
printf("Decommitting the second chunk\n");
if (!VirtualFree(chunk2, (size_t)PAGES_PER_CHUNK*pageSize, MEM_DECOMMIT)) {
printf("Failed to decommit!\n");
}
Sleep(5000);
}
printf("Re-committing the second chunk\n");
if (!VirtualAlloc(chunk2, (size_t)PAGES_PER_CHUNK*pageSize, MEM_COMMIT, PAGE_READWRITE)) {
printf("Failed to recommit!\n");
}
Sleep(5000);
} break;
case 3: {
printf("ZeroMemory()-ing the second chunk\n");
ZeroMemory(chunk2, (size_t)PAGES_PER_CHUNK*pageSize);
Sleep(5000);
} break;
case 4: {
printf("VirtualUnlock()-ing the second chunk\n");
if (!VirtualUnlock(chunk2, (size_t)PAGES_PER_CHUNK*pageSize)) {
printf("VirtualUnlock \"failed\", but I think this is normal.\n");
}
Sleep(5000);
} break;
case 5: {
printf("DiscardVirtualMemory()-ing the second chunk\n");
if (DiscardVirtualMemory(chunk2, (size_t)PAGES_PER_CHUNK*pageSize) != ERROR_SUCCESS) {
printf("DiscardVirtualMemory failed!\n");
}
Sleep(5000);
} break;
}
//
// READ FROM THE SECOND CHUNK
//
printf("Reading, not writing, from the second chunk\n");
int sum = 0;
for (size_t i = 0; i < (size_t)PAGES_PER_CHUNK*pageSize; i++) {
sum += chunk2[i];
}
printf("The sum of the data is %d\n", sum);
if (sum == 0) {
printf("The pages were zeroed, as expected.\n");
} else {
printf("The pages were NOT zeroed! This strategy does not work!\n");
}
Sleep(5000);
//
// TOUCH THE SECOND CHUNK
//
printf("Touching everything in the second chunk again\n");
if (STRATEGY == 0) {
printf("MEM_RESET_UNDO-ing the second chunk\n");
if (!VirtualAlloc(chunk2, (size_t)PAGES_PER_CHUNK*pageSize, MEM_RESET_UNDO, PAGE_READWRITE)) {
printf("MEM_RESET_UNDO failed, so some pages were reclaimed.\n");
}
Sleep(5000);
}
for (size_t i = 0; i < PAGES_PER_CHUNK; i++) {
chunk2[i*pageSize] = (char)(-i);
}
printf("The second chunk has now been touched.\n");
Sleep(5000);
printf("sleepy time\n");
Sleep(60000);
printf("I die\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment