C#编译器或JITter可以进行优化会产生明显的副作用吗?
我离开的一个例子。
var x = new Something();
A(x);
B(x);
在A(x)
结束时,保证将x
A
保持活跃状态 - 因为B
使用相同的参数。但是如果B
被定义为
public void B(Something x) { }
然后优化器可以删除B(x)
,然后可能需要进行GC.KeepAlive(x)
调用。
这个优化实际上可以由JITter完成吗?
除了堆栈跟踪更改之外,还有其他优化可能会产生明显的副作用吗?
答案 0 :(得分:8)
如果你的功能B不使用参数x,那么消除它并早期收集x 没有任何可见的副作用。
要成为“可见的副作用”,它们必须是到程序,而不是像调试器或对象查看器这样的外部工具。
答案 1 :(得分:6)
当调用A(x)x时,保证在A结束时保持活着 - 因为B使用相同的参数。
这句话是错误的。假设方法A总是抛出异常。抖动可能知道B永远不会到达,因此x可以立即释放。假设方法A在最后一次引用x之后进入无条件无限循环;再次,抖动可以知道通过静态分析,确定x将永远不会被再次引用,并安排它被清理。我不知道抖动是否实际执行了这些优化;他们似乎很狡猾,但他们是合法的。
这种优化(即,尽早清理未在任何地方使用的引用)实际上可以由JITter完成吗?
是的,实际上已经完成了。这不是一个可观察到的副作用。
规范的第3.9节证明了这一点,为方便起见,我引用了它:
如果对象或其任何部分无法通过任何可能的继续执行来访问,除了运行析构函数之外,该对象被认为不再使用,并且它有资格进行销毁。 C#编译器和垃圾收集器可以选择分析代码以确定将来可以使用对对象的哪些引用。例如,如果范围内的局部变量是对象的唯一现有引用,但该过程中当前执行点的任何可能的继续执行中从不引用该局部变量,则垃圾收集器可能(但是不要求将对象视为不再使用。
C#编译器或JITter可以进行优化会产生明显的副作用吗?
您的问题在规范的第3.10节中得到解答,为方便起见,我在此引用:
继续执行C#程序 这样每个人的副作用 执行线程保留在 关键执行点。
一方 效果被定义为读或写 一个易失性字段,一个写入 非易失性变量,写入 外部资源,投掷 一个例外。
关键执行 这些顺序的点 必须保留副作用 对volatile字段的引用,锁定语句, 和线程创建和终止。
执行环境是免费的 改变C#的执行顺序 计划,以下为准 约束:
数据依赖性 保留在一个线程中 执行。也就是每个人的价值 变量计算就像全部一样 线程中的语句已执行 按照原始程序顺序。
初始化排序规则是 保存。
在 保留副作用的排序 关于易失性读取和 写道。
另外, 执行环境不需要 如果它,则评估表达式的一部分 可以推断出那个表达式 没有使用值,也没有必要 产生副作用(包括 通过调用方法或引起的任何问题 访问volatile字段)。
当 程序执行被中断 异步事件(例如 另一个线程引发的异常), 它不能保证 可观察到的副作用是可见的 原始程序订单。
倒数第二段是我相信你最关心的一段;也就是说,在影响可观察的副作用方面,允许运行时的优化是什么?允许运行时执行任何优化,这不会影响可观察的副作用。
请注意,特别是数据依赖性仅在执行线程中保留。当从另一个执行线程观察时,数据依赖性 not 保证保留。
如果这不能回答您的问题,会提出更具体的问题。特别是,如果您不认为上面给出的定义符合您对“可观察到的副作用”的定义,则需要仔细而精确地定义“可观察到的副作用”以更详细地回答您的问题。
答案 2 :(得分:0)
在您的问题中加入B
只是让问题混乱。鉴于此代码:
var x = new Something();
A(x);
假设A(x)
是托管代码,则调用A(x)
会保留对x
的引用,因此垃圾收集器在{{1}之后才能收集x
}返回。或者至少在A
不再需要它之前。 JITer所做的优化(缺少错误)不会过早收集A
。
您应该通过“可见的副作用”来定义您的意思。人们希望JITer优化至少会产生使代码更小或更快的副作用。那些“可见吗?”或者你的意思是“不受欢迎?”
答案 3 :(得分:0)