D如何支持写障碍

原文
我开始思考编译器中的指针障碍.不把它们看作新类型,而是当作类似切片中的增加了安全性和功能检查区间,但在特例下,如果必要,可在本地和全局绕过它们.
你仍然拥有今天D所拥有的指针.唯一区别是添加了一种绕过GC写障碍的方法,可能类似:

core.memory.__raw(T)
//
struct __raw(T) {
    T ptr;
    alias ptr this;
    auto opAssign(T rhs) { /*绕过边界检查*/ } 
}

注意别名 本.如果真有这样隐式转换回普通指针的指针.
另一方面,构造时,总是显式绕过检查.唯一工作是表明你想绕过正常检查,甚至可类似

slice.ptr[0]

本地绕过边界检查.你不必真正用它;严格说是一种可选的性能增强,用于就像可绕过边界检查一样的特例.

编译器中,同样类似边界检查,可添加:

-barrier=[none|writes]

命令行开关来全局控制它.如果没有编译屏障,不能用一些GC实现.
屏障自身被转发给druntime函数或llvm内部函数,或适合编译器和垃集的函数(这会使运行时交换出收集器成为额外性能损失,因为屏障也是函数指针,但这是合理决定).
函数像:

core.memory.__ptr_write(void** where, void* what)

因此,如果实现有最大灵活性.用内部函数和内联,当关闭屏障时,会化简为:

mov [where], what;

注意:

1,因为从未写入常指针,它不需要写屏障.

const T* a; a = something;

前端不需要特别注意,因为编译不过.
2,自赋值指针,

int* a; a++; 
int[] a;
a = a[1 .. $];

也不需要写屏障.因为目的是保护改变GC行为的竞争条件,而GC只关心它指向的块,这些操作本身禁止改变它指向的块.如果切片越界,则通不过检查,甚至根据C规则,原始指针超出原始赋值块也是未定义行为.
只需确保是原子的替换实际指针;确保实际生成inc ptr,而不是读-改-写.我肯定编译器已这样做了,但必须指定来确保.

编译器前端可闲着,因为后端可检测到这些自修改并消除检查.

我想,即即使启用它,很多D函数也根本不会生成障碍除非遗漏了些重点.你重新赋值指针到另一块时,就会生成障碍.
3,也可消除借用引用的障碍,因为你知道GC会看到它在其他地方的规范引用.关键是:写操作会影响GC在运行过程中的行为吗?如果是,则需要保护它.如果不是,可跳过它.安全最好;不需要的额外障碍对性能会造成很小影响,而缺少需要的障碍则是严重运行时错误.
4,省略了保守的栈扫描,和复制/移动GC实现.但对世代GC和大多数增量和并发GC的实现足够了.当然,需要命令行开关来生成更多代码,来支持其他方案,但是我勾画出可能工作的最简单的.

5,上面都仅适用直接写至指针,而不是通过指针写.

char* a;
a = x; // 有写障
*a = x; // 没有.

指针值变化才是重要的,GC不关心随机字符值,只关心引用和未引用哪些内存块.

不同GC实现的好处:
当前,GC标记阶段停止所有线程,这样就不会竞争,当它标记时,如果GC其他地方工作时,它会变化.有了写障,除非实际写指针,可避免停止线程.
假设音频线程正在消耗GC资源,但只写入到现有的ushort[]缓冲区,它的活动永远不会使GC工作进程失效,所以可继续运行音频线程.这不需要显式用户工作,也不需要注销它.
另一个工作线程也许正在处理一些数据,然后发送缓冲一个函数.根据借用指针是否管用,它可能一直运行,但是更简单的,更少漏洞实现会让它完成处理数据,然后只在复制缓冲指针到函数时才因为触发写屏障真正停止.到那时,GC可能几乎完成了,即实际停止时间比当前实现要小得多.

写障碍是完全的净收入.

posted @   zjh6  阅读(12)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示