Last active
August 4, 2023 02:08
-
-
Save bvisness/b29fe28048645c49fdb4d4e86b7018a9 to your computer and use it in GitHub Desktop.
memory.discard test programs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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