Tokio Gives Progress, Not Ordering: Scheduling 1M Tasks2 months agohttps://pranitha.dev/posts/tokio-gives-progress-not-ordering/Tokio的多线程调度器使用本地和全局队列,但不保证任务按创建顺序被轮询。提前生成任务并不能保证提前轮询或完成;来自不同事件的任务竞争工作队列,可能会被延迟。如果没有对任务创建的限制,大量活跃任务会增加峰值内存,因为较早的任务及其父任务存活时间更长。使用信号量限制并发事件可以实现事件级公平性,在保持吞吐量的同时减少内存峰值。应用程序代码必须自行实施公平性边界;Tokio对所有任务一视同仁,不了解事件等应用级单元。
Span-First C#: Designing Around Span<T>2 months agohttps://slicker.me/c_sharp/span-first.htmlSpan<T> 是一个ref结构体,提供对连续内存的类型安全、边界检查的视图,而不拥有或复制内存。它可以包装数组、字符串、stackalloc块,以及通过MemoryMarshal管理的非托管内存。Span<T> 仅限于栈上使用,避免堆分配并确保GC重定位的安全性。四种相关类型(Span<T>、ReadOnlySpan<T>、Memory<T>、ReadOnlyMemory<T>)覆盖了可变/只读和栈/堆两个维度。Memory<T> 是支持堆存储的对应类型,适用于异步场景,而Span<T> 用于同步场景。Span<T> 的ref struct限制在编译时强制生命周期管理,禁止字段存储、装箱、async/await和闭包。C# 13允许ref struct作为泛型类型参数约束,但未解除字段、闭包或async限制。切片操作在不复制的情况下生成一个新的Span<T> 子范围,使用索引和范围语法。stackalloc与Span<T> 结合提供栈上分配的缓冲区,无GC压力,但仅限于当前栈帧,通常不超过1-2 KB。Span优先的解析通过在原始缓冲区上遍历索引来避免分配,仅在最终消费时才生成字符串或数字。SearchValues<T>(.NET 8+)优化了分词器和分隔符扫描中的重复IndexOfAny调用。Span优先的API设计以ReadOnlySpan<T> 作为规范重载,数组/字符串重载作为便利包装器。MemoryMarshal允许在类型边界间重新解释跨度,例如无需复制即可将字节缓冲区转换为结构跨度。常见失败模式:在异步方法中使用Span<T>;修复方法是在异步层使用Memory<T>,在同步叶节点使用Span<T>。Span优先的解析相比基于字符串的解析在性能和内存方面有显著优势,零分配且更低延迟。安全注意事项包括:编译器能捕获stackalloc跨度的直接逃逸,但无法捕获所有间接逃逸;切片边界检查虽然廉价但并非零成本。以字符串为键的字典无法直接使用ReadOnlySpan<char> 而不调用ToString(),但.NET 9提供了GetAlternateLookup用于跨度键查询。Span<T> 未实现IEnumerable<T>,因此无法使用LINQ方法,需要手动实现。
Why Huge Pages matter for Postgres2 months agohttps://clickhouse.com/blog/huge-pages-clickhouse-managed-postgres大页是较大的内存单元(2MB 或 1GB),相比标准的 4KB 页面,它允许一个页表项覆盖更多的内存,从而减少了 CPU 的 TLB 必须缓存的转换次数。Postgres 对页面大小很敏感,因为它的 shared_buffers 缓存是由每个后端的页表映射的;使用 4KB 页面时,大型缓存可能会消耗过多的 RAM 用于页表,并导致 TLB 未命中,从而降低读取速度。使用大页(例如 2MB)可以显著减少每个连接的页表内存使用量——在测试中,从每个连接约 31MB 降至约 0.5MB——并提高 TLB 覆盖率,将热点工作集保持在缓存中,从而提升吞吐量。ClickHouse Managed Postgres 通过预先保留内存(RAM 的 25%),配置 huge_pages = 'on' 以防止静默回退,并通过测量而非假设来调整 shared_buffers 的大小以适合保留的内存池,从而确保使用大页。在 EC2 实例上的测试显示,使用 200 个连接时,4KB 页面导致 6.12GB 的页表,而使用 2MB 大页时仅为 111MB;由于减少了转换开销,使用大页时吞吐量提高了 12%。