Mercurial > hg
comparison rust/hg-core/src/revlog/mod.rs @ 52181:3d797007905d
rust: populate mmaps in a separate thread if possible
Same rationale as b619ba39d10a.
author | Raphaël Gomès <rgomes@octobus.net> |
---|---|
date | Thu, 01 Aug 2024 11:27:20 +0200 |
parents | 1da6995835b4 |
children | bd8081e9fd62 |
comparison
equal
deleted
inserted
replaced
52180:1032bb0ef365 | 52181:3d797007905d |
---|---|
444 } | 444 } |
445 } | 445 } |
446 | 446 |
447 type IndexData = Box<dyn Deref<Target = [u8]> + Send + Sync>; | 447 type IndexData = Box<dyn Deref<Target = [u8]> + Send + Sync>; |
448 | 448 |
449 /// TODO We should check for version 5.14+ at runtime, but we either should | |
450 /// add the `nix` dependency to get it efficiently, or vendor the code to read | |
451 /// both of which are overkill for such a feature. If we need this dependency | |
452 /// for more things later, we'll use it here too. | |
453 #[cfg(target_os = "linux")] | |
454 fn can_advise_populate_read() -> bool { | |
455 true | |
456 } | |
457 | |
458 #[cfg(not(target_os = "linux"))] | |
459 fn can_advise_populate_read() -> bool { | |
460 false | |
461 } | |
462 | |
463 /// Call `madvise` on the mmap with `MADV_POPULATE_READ` in a separate thread | |
464 /// to populate the mmap in the background for a small perf improvement. | |
465 #[cfg(target_os = "linux")] | |
466 fn advise_populate_read_mmap(mmap: &memmap2::Mmap) { | |
467 const MADV_POPULATE_READ: i32 = 22; | |
468 | |
469 // This is fine because the mmap is still referenced for at least | |
470 // the duration of this function, and the kernel will reject any wrong | |
471 // address. | |
472 let ptr = mmap.as_ptr() as u64; | |
473 let len = mmap.len(); | |
474 | |
475 // Fire and forget. The `JoinHandle` returned by `spawn` is dropped right | |
476 // after the call, the thread is thus detached. We don't care about success | |
477 // or failure here. | |
478 std::thread::spawn(move || unsafe { | |
479 // mmap's pointer is always page-aligned on Linux. In the case of | |
480 // file-based mmap (which is our use-case), the length should be | |
481 // correct. If not, it's not a safety concern as the kernel will just | |
482 // ignore unmapped pages and return ENOMEM, which we will promptly | |
483 // ignore, because we don't care about any errors. | |
484 libc::madvise(ptr as *mut libc::c_void, len, MADV_POPULATE_READ); | |
485 }); | |
486 } | |
487 | |
488 #[cfg(not(target_os = "linux"))] | |
489 fn advise_populate_read_mmap(mmap: &memmap2::Mmap) {} | |
490 | |
449 /// Open the revlog [`Index`] at `index_path`, through the `store_vfs` and the | 491 /// Open the revlog [`Index`] at `index_path`, through the `store_vfs` and the |
450 /// given `options`. This controls whether (and how) we `mmap` the index file, | 492 /// given `options`. This controls whether (and how) we `mmap` the index file, |
451 /// and returns an empty buffer if the index does not exist on disk. | 493 /// and returns an empty buffer if the index does not exist on disk. |
452 /// This is only used when doing pure-Rust work, in Python contexts this is | 494 /// This is only used when doing pure-Rust work, in Python contexts this is |
453 /// unused at the time of writing. | 495 /// unused at the time of writing. |
463 { | 505 { |
464 let size = store_vfs.file_size(&file)?; | 506 let size = store_vfs.file_size(&file)?; |
465 if size >= threshold { | 507 if size >= threshold { |
466 // TODO madvise populate read in a background thread | 508 // TODO madvise populate read in a background thread |
467 let mut mmap_options = MmapOptions::new(); | 509 let mut mmap_options = MmapOptions::new(); |
468 // This does nothing on platforms where it's not defined | 510 if !can_advise_populate_read() { |
469 mmap_options.populate(); | 511 // Fall back to populating in the main thread if |
512 // post-creation advice is not supported. | |
513 // Does nothing on platforms where it's not defined. | |
514 mmap_options.populate(); | |
515 } | |
470 // Safety is "enforced" by locks and assuming other | 516 // Safety is "enforced" by locks and assuming other |
471 // processes are well-behaved. If any misbehaving or | 517 // processes are well-behaved. If any misbehaving or |
472 // malicious process does touch the index, it could lead | 518 // malicious process does touch the index, it could lead |
473 // to corruption. This is somewhat inherent to file-based | 519 // to corruption. This is somewhat inherent to file-based |
474 // `mmap`, though some platforms have some ways of | 520 // `mmap`, though some platforms have some ways of |
475 // mitigating. | 521 // mitigating. |
476 // TODO linux: set the immutable flag with `chattr(1)`? | 522 // TODO linux: set the immutable flag with `chattr(1)`? |
477 let mmap = unsafe { mmap_options.map(&file) } | 523 let mmap = unsafe { mmap_options.map(&file) } |
478 .when_reading_file(index_path)?; | 524 .when_reading_file(index_path)?; |
525 | |
526 if can_advise_populate_read() { | |
527 advise_populate_read_mmap(&mmap); | |
528 } | |
529 | |
479 Some(Box::new(mmap) as IndexData) | 530 Some(Box::new(mmap) as IndexData) |
480 } else { | 531 } else { |
481 None | 532 None |
482 } | 533 } |
483 } else { | 534 } else { |