{"id":19602337,"url":"https://github.com/scottlamb/page-primer","last_synced_at":"2025-04-27T17:32:14.334Z","repository":{"id":254184609,"uuid":"845727752","full_name":"scottlamb/page-primer","owner":"scottlamb","description":"speeds up your Rust program's execution by \"priming\" memory pages from your binary","archived":false,"fork":false,"pushed_at":"2024-08-22T20:43:15.000Z","size":23,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T08:37:07.128Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scottlamb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-21T20:11:55.000Z","updated_at":"2025-02-19T13:28:35.000Z","dependencies_parsed_at":"2024-08-21T23:18:02.112Z","dependency_job_id":"8e3063b9-7ee0-44df-aea1-d03118ea35bc","html_url":"https://github.com/scottlamb/page-primer","commit_stats":null,"previous_names":["scottlamb/page-primer"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottlamb%2Fpage-primer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottlamb%2Fpage-primer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottlamb%2Fpage-primer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottlamb%2Fpage-primer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scottlamb","download_url":"https://codeload.github.com/scottlamb/page-primer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251177999,"owners_count":21548143,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T09:23:35.867Z","updated_at":"2025-04-27T17:32:14.100Z","avatar_url":"https://github.com/scottlamb.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `page-primer`\n\n`page-primer` speeds up your Rust program's execution by \"priming\"\nmemory pages from your binary. It supports two optimizations:\n\n*   `mlock()`ing ELF segments so they are never paged out, avoiding \"major page\n    fault\" stalls while waiting for them to be read back in from SSD or even\n    spinning disk.\n*   remapping ELF segments to enable huge pages, which can speed up large\n    programs by 5–10%. (More below.)\n\n`page-primer` only does anything on Linux at present, though at least the\nconcept of `mlock()` should apply to any Unix-based system.\n\n## When should you use it?\n\n*   in *applications*, not *libraries*\n*   that run on *Linux*\n*   and are *long-running*\n*   if you care about *CPU efficiency* and/or *latency*\n*   and you can afford some *extra RAM*\n*   and you can accept the *`unsafe`* blocks\n\n## How do you use it?\n\n1.  Near the top of `main()`, before spawning any threads, add this code:\n    ```rust\n    let prime_out = page_primer::prime()\n        .mlock(true)\n        .remap(true) // if desired, see notes.\n        .run();\n    ```\n2.  Further down `main()`, after logging providers have been set up, add this code:\n    ```rust\n    prime_out.log();\n    ```\n3. If using remap, add the following to your\n   [`.cargo/config.toml`](https://doc.rust-lang.org/cargo/reference/config.html):\n   ```toml\n   [target.x86_64-unknown-linux-gnu]\n   rustflags = [\n       \"-C\", \"link-arg=-z\",\n       \"-C\", \"link-arg=common-page-size=2097152\",\n       \"-C\", \"link-arg=-z\",\n       \"-C\", \"link-arg=max-page-size=2097152\",\n   ]\n   ```\n4. Verify the performance improvement!\n\nOne caveat is that if you later `dlopen` some dynamic library, this code will\nnot know to prime it.\n\n## Remapping and huge pages\n\n### Background on virtual memory: pages, huge pages, and transpage huge pages\n\nModern OSs/CPUs use [virtual memory](https://en.wikipedia.org/wiki/Virtual_memory):\nmemory addresses seen by userspace processes don't directly represent physical\nmemory locations. Instead, the virtual address space is divided into \"pages\"\nwhose meaning is defined by \"page tables\" maintained by the OS. The CPU's\nMemory Mapping Unit (MMU) consults the page tables to translate virtual memory\naddresses to physical memory addresses. This mapping helps isolate processes\nfrom each other for security and reliability, among other benefits.\n\nAs system RAM sizes have grown to gigabytes and beyond, the page size generally\nhasn't changed: it's still 4 KiB on Linux/x86_64. This is a problem! The page\ntables have gotten too big for CPUs to consult quickly. While CPUs cache\npage tables in a Translation Lookaside Buffer (TLB), they often spend 15% of\ntheir time stalled on TLB cache misses.\n\nThe solution is larger pages. Linux/x86_64's still uses 4 KiB pages by default\nbut also supports 2 MiB or 1 GiB [huge\npages](https://www.kernel.org/doc/html/latest/admin-guide/mm/hugetlbpage.html).\nThese represent 256 or 262,144 as much RAM for the same TLB space. When they're\nused extensively, far less of the CPU's time is spent waiting on TLB misses.\n\nLinux even supports [transparent huge\npages (THP)](https://www.kernel.org/doc/html/latest/admin-guide/mm/transhuge.html)\nwhich are used automatically. But only sometimes. It will not use\ntransparent huge pages on file-backed mappings unless your kernel was compiled\nwith the experimental option\n[`CONFIG_READ_ONLY_THP_FOR_FS=y`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/Kconfig?h=v6.11-rc4\u0026id=47ac09b91befbb6a235ab620c32af719f8208399#n861).\nMost distro kernels are not. That means your program's executable will not take\nadvantage of huge pages. Unless...\n\n### Remapping for huge pages\n\nPrograms can start up, create a new huge page-eligible memory mapping, copy\ntheir code over to it, and remap it over the place of their existing code.\nThis idea is saner than it sounds and has been around for a while:\n\n*   [libhugetlbfs](https://github.com/libhugetlbfs/libhugetlbfs) has supported\n    this idea since 2005. But it uses `hugetlbfs`, which is finicky. The system\n    administrator has to arrange for pages to be reserved on startup and a\n    special filesystem to be mounted.\n*   Google remaps via anonymous `mmap` for many servers running in its\n    datacenters and [on ChromeOS](https://chromium.googlesource.com/chromium/src/+/66.0.3359.158/chromeos/hugepage_text/hugepage_text.cc).\n*   Facebook remaps via anonymous `mmap` [in HHVM](https://github.com/facebook/hhvm/blob/b3b1562e17f2cedcfbf431f86f492cbdc3988f91/hphp/runtime/base/program-functions.cpp).\n\n`page-primer`'s implementation uses `memfd_create`. Before, `/proc/\u003cpid\u003e/maps`\nmight look like this:\n\n```text\n5646c542d000-5646c55ef000 r--p 00000000 103:03 69612122                  /home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr\n5646c562d000-5646c66f3000 r-xp 00200000 103:03 69612122                  /home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr\n5646c682d000-5646c6d74000 r--p 01400000 103:03 69612122                  /home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr\n5646c7143000-5646c722d000 r--p 01b16000 103:03 69612122                  /home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr\n5646c722d000-5646c7230000 rw-p 01c00000 103:03 69612122                  /home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr\n...\n```\n\nAfterward, it will look like this:\n\n```text\n5646c5400000-5646c542d000 r--p 00000000 00:01 25220866                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c542d000-5646c55ef000 r--p 0002d000 00:01 25220866                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c55ef000-5646c5600000 r--p 001ef000 00:01 25220866                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c5600000-5646c562d000 r-xp 00000000 00:01 25220867                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c562d000-5646c66f3000 r-xp 0002d000 00:01 25220867                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c66f3000-5646c6800000 r-xp 010f3000 00:01 25220867                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c6800000-5646c682d000 r--p 00000000 00:01 25220868                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c682d000-5646c6d74000 r--p 0002d000 00:01 25220868                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c6d74000-5646c6e00000 r--p 00574000 00:01 25220868                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c7000000-5646c7143000 rw-p 00000000 00:01 25220869                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c7143000-5646c7231000 rw-p 00143000 00:01 25220869                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n5646c7231000-5646c7400000 rw-p 00231000 00:01 25220869                   /memfd:/home/slamb/git/moonfire-nvr/server/target/debug/moonfire-nvr (deleted)\n```\n\nThere is one significant downside: this remapping can break some debugging and\nprofiling tools' ability to get back traces. Please let me know if you're\naware of an approach that can solve this problem!\n\n### Troubleshooting\n\nRemapping works best if your program's `LOAD` sections are aligned to a\nmultiple of your platform's transparent huge page size. The `.cargo/config.toml`\nsnippet above should accomplish this. You can verify it worked via `readelf`:\n\n```text\n$ readelf --segments target/debug/examples/simple\n...\nProgram Headers:\n  Type           Offset             VirtAddr           PhysAddr\n                 FileSiz            MemSiz              Flags  Align\n...\n  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000\n                 0x0000000000035308 0x0000000000035308  R      0x200000\n  LOAD           0x0000000000200000 0x0000000000200000 0x0000000000200000\n                 0x000000000020d391 0x000000000020d391  R E    0x200000\n  LOAD           0x0000000000600000 0x0000000000600000 0x0000000000600000\n                 0x0000000000079468 0x0000000000079468  R      0x200000\n  LOAD           0x00000000007e5d60 0x00000000009e5d60 0x00000000009e5d60\n                 0x000000000001a338 0x000000000001a580  RW     0x200000\n...\n```\n\nFinally, the kernel should actually load the code with alignment that is\na multiple of these boundaries, as verified with `/proc/\u003cpid\u003e/maps`. This should\nhappen with Linux kernels 5.10 and above. More precisely, your kernel should\nhave commit\n[`ce81bb256a224259ab686742a6284930cbe4f1fa`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ce81bb256a224259ab686742a6284930cbe4f1fa).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscottlamb%2Fpage-primer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscottlamb%2Fpage-primer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscottlamb%2Fpage-primer/lists"}