NET9Pre7DATAS+RustcCompile线程续

程序员有二十年 2024-08-26 13:12:35
.NET9 PreView7 DATAS

.NET 9中引入的动态适应应用程序大小(DATAS)功能。DATAS旨在根据应用程序的内存需求自动调整堆大小,使其与长期存活数据的大小大致成正比。这与现有的服务器垃圾回收(Server GC)模式有所不同,后者关注的是提高吞吐量,而不是根据应用程序的大小来调整堆。

DATAS的优势在于:适应不同硬件配置下的应用程序堆大小,使堆大小在不同规格的机器上保持一致或相似。根据工作负载的变化自动调整堆大小,特别是在内存受限的环境中,这有助于在某些进程的工作负载减轻时容纳更多进程。有助于容量规划。

DATAS功能的实现方法包括:

根据长期存活数据的大小设置触发下一次垃圾回收前的最大分配量,以限制堆大小。根据吞吐量设置实际允许的分配量。根据需要调整堆的数量,实现工作站垃圾回收(最少一个堆)和服务器垃圾回收(与机器核心数匹配)之间的混合模式。在需要时执行完全压缩垃圾回收,以防止碎片过多,同时也有助于限制堆大小。

基准测试结果显示,在48核Linux机器上运行基准测试时,工作集显著减少,最大吞吐量(以rps计)降低了约2-3%,但工作集改善了80%以上。启用DATAS后,Gen0和Gen1垃圾回收次数显著增加。

Rust Compile线程详细

继续Rustc的Compile线程,由于llvm或者rustc底板编译器生成的汇编格式在优化之后,一时还不太习惯。一般都类似于这种:

mov rax [rip+0x100]call rax

而传递的参数,往往是不是rdi,rsi这种,它直接用rip+偏移取代了。这种情况在rustc里面到处都是。

创建一个线程,这里的新线程入口是thread_start,参数是p。:

///rust/library/std/src/sys/pal/unix/thread.rs:84let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _);

下面的main参数应是上面的p参

//library/std/src/sys/pal/unix/thread.rsextern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { unsafe { let _handler = stack_overflow::Handler::new(); Box::from_raw(main as *mut Box<dyn FnOnce()>)(); } ptr::_mut()}

as *mut Box<dyn FnOnce()> 将 main 函数的地址转换为一个指向 Box<dyn FnOnce()> 类型的可变指针。Box::from_raw 是 Rust 标准库中的一个函数,用于从原始指针创建一个 Box<T>。这里它用于从 main 函数的地址创建一个指向 Box<dyn FnOnce()> 的 Box。

FnOnce如下:

// library/core/src/ops/function.rs#[lang = "fn_once"]#[stable(feature = "rust1", since = "1.0.0")]#[rustc_paren_sugar]#[rustc_on_unimplemented( on( Args = "()", note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}`" ), on( _Self = "unsafe fn", note = "unsafe function cannot be called generically without an unsafe block", // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), message = "expected a `{Trait}` closure, found `{Self}`", label = "expected an `{Trait}` closure, found `{Self}`")]#[fundamental] // so that regex can rely that `&str: !FnMut`#[must_use = "closures are lazy and do nothing unless called"]// FIXME(effects) #[const_trait]pub trait FnOnce<Args: Tuple> { /// The returned type after the call operator is used. #[lang = "fn_once_output"] #[stable(feature = "fn_once_output", since = "1.12.0")] type Output; /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call_once(self, args: Args) -> Self::Output;}

其call_once调用如下:

//library/std/src/sys/backtrace.rs:154#[inline(never)]pub fn __rust_begin_short_backtrace<F, T>(f: F) -> Twhere F: FnOnce() -> T,{ let result = f(); std::hint::black_box(()); result}

这里就体现了上面说的ASM格式,比如f():

0x7ffff0b86530 <+0>: push rax-> 0x7ffff0b86531 <+1>: call qword ptr [rip + 0x700a7e9] 0x7ffff0b86537 <+7>: and al, 0x1 0x7ffff0b86539 <+9>: pop rcx 0x7ffff0b8653a <+10>: ret

通过传递的参数调取相应的处理,比如IR变异,机器码生成等等。有的rust函数调用了glibc,比如f(),如果感到疑惑,可以di命令查看其所有汇编代码分析。

0 阅读:0

程序员有二十年

简介:感谢大家的关注