// Count how many pages of a file are resident in the page cache (mincore(2)). // This is how we check "did the pager really bypass the page cache?": if the // expert weights were served from cache, the model file would show up as resident. // // usage: pagecache_resident [file...] #include #include #include #include #include #include #include #include int main(int argc, char ** argv) { if (argc < 2) { fprintf(stderr, "usage: %s [--evict] [file...]\n", argv[0]); return 1; } int argi = 1; int evict = 0; if (strcmp(argv[1], "--evict") == 0) { evict = 1; argi = 2; } const long page = sysconf(_SC_PAGESIZE); unsigned long long tot_r = 0, tot_s = 0; for (int i = argi; i < argc; i++) { const char * path = argv[i]; int fd = open(path, O_RDONLY); if (fd < 0) { perror(path); return 1; } struct stat st; fstat(fd, &st); if (evict) { // per-file eviction of clean page-cache pages (no root needed) posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED); } const size_t size = (size_t) st.st_size; char * addr = (char *) mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { perror("mmap"); return 1; } const size_t npages = (size + page - 1) / page; unsigned char * vec = (unsigned char *) malloc(npages); if (mincore(addr, size, vec) != 0) { perror("mincore"); return 1; } unsigned long long res = 0; for (size_t p = 0; p < npages; p++) { res += (vec[p] & 1); } free(vec); munmap(addr, size); close(fd); const char * base = strrchr(path, '/'); base = base ? base + 1 : path; printf("%-48s resident %8.3f GiB / %8.3f GiB (%5.1f %%)\n", base, res * (double) page / 1073741824.0, size / 1073741824.0, 100.0 * (double) res * (double) page / (double) size); tot_r += res * (unsigned long long) page; tot_s += size; } if (argc > argi + 1) { printf("%-48s resident %8.3f GiB / %8.3f GiB\n", "TOTAL", tot_r / 1073741824.0, tot_s / 1073741824.0); } return 0; }