点击上方蓝字 江湖评谈设为关注/星标
.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:84
let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _);
下面的main参数应是上面的p参
//library/std/src/sys/pal/unix/thread.rs
extern "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::null_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) -> T
where
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命令查看其所有汇编代码分析。
往期精彩回顾