栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

在写入变量之前检查变量是否具有特定值是否明智?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

在写入变量之前检查变量是否具有特定值是否明智?

是的,在某些情况下肯定是明智的,并且正如您所建议的那样,volatile变量就是其中一种情况,即使对于单线程访问也是如此!

从硬件和编译器/
JIT的角度来看,易失性写入都很昂贵。在硬件级别上,这些写操作可能比普通写操作贵10到100倍,因为必须清空写缓冲区(在x86上,具体信息因平台而异)。在编译器/
JIT级别,易失性写会抑制许多常见的优化。

但是,投机只能带给您如此远的好处-
证明始终在基准测试中。这是一个微基准测试,可尝试您的两种策略。基本思想是将值从一个数组复制到另一个数组(几乎是System.arraycopy),具有两个变体-
一个变体无条件复制,另一个变体首先检查值是否不同。

这是简单的非易失性情况的复制例程(此处提供完整源代码):

        // no check        for (int i=0; i < ARRAY_LENGTH; i++) { target[i] = source[i];        }        // check, then set if unequal        for (int i=0; i < ARRAY_LENGTH; i++) { int x = source[i]; if (target[i] != x) {     target[i] = x; }        }

使用Caliper作为我的微基准测试工具,使用上述代码复制数组长度为1000的结果是:

    benchmark arrayType    ns linear runtime  CopyNoCheck      SAME   470 =  CopyNoCheck DIFFERENT   460 =    CopyCheck      SAME  1378 ===    CopyCheck DIFFERENT  1856 ====

每次运行还需要大约150 ns的开销,才能每次重置目标阵列。跳过检查要快得多-每个元素大约0.47 ns(或者在除去设置开销后每个元素大约0.32
ns,所以我的盒子上几乎恰好1个周期)。

当阵列相同时,检查速度要慢大约3倍,而不同时则要慢4倍。鉴于支票被完美预测,我对支票有多糟糕感到惊讶。我怀疑罪魁祸首很大程度上是JIT-
循环主体更加复杂,展开的次数可能更少,并且其他优化可能不适用。

让我们切换到易失性案例。在这里,我用作

AtomicIntegerArray
易失性元素的数组,因为Java没有任何带有易失性元素的本机数组类型。在内部,此类仅使用进行直接写入数组
sun.misc.Unsafe
,从而允许进行易失性写入。生成的程序集与易失性方面(以及可能的范围检查消除,在AIA情况下可能无效)不同,基本上类似于常规阵列访问。

这是代码:

        // no check        for (int i=0; i < ARRAY_LENGTH; i++) { target.set(i, source[i]);        }        // check, then set if unequal        for (int i=0; i < ARRAY_LENGTH; i++) { int x = source[i]; if (target.get(i) != x) {     target.set(i, x); }        }

结果如下:

arrayType     benchmark    us linear runtime     SAME   CopyCheckAI  2.85 =======     SAME CopyNoCheckAI 10.21 ===========================DIFFERENT   CopyCheckAI 11.33 ==============================DIFFERENT CopyNoCheckAI 11.19 =============================

桌子已经翻了。首先检查比通常的方法快约3.5倍。总体而言,一切都慢得多-在检查情况下,每个循环我们要付出约3 ns的代价,在最坏的情况下,我们要付出约10
ns的代价(上面的时间在我们身上,涵盖了整个1000个元素数组的副本)。易失性写入确实更昂贵。DIFFERENT情况下包含大约1
ns的开销,以在每次迭代时重置阵列(这就是为什么对于DIFFERENT而言,即使简单也稍微慢一点)的原因。我怀疑“检查”情况下的许多开销实际上是边界检查。

这都是单线程的。如果您实际上对volatile有跨核心的争用,那么对于简单方法而言,结果将是非常糟糕的得多,并且与上述检查情况一样好(缓存行仅处于共享状态-
否所需的一致性流量)。

我也只测试了“每个元素相等”与“每个元素不同”的极端。这意味着“检查”算法中的分支总是可以完美预测的。如果混合使用相等和不同,那么您将不会仅获得SAME和DIFFERENT情况下时间的加权组合-
由于预测错误(在硬件级别,甚至在JIT级别),您的情况会更糟,无法再针对始终采用的分支进行优化)。

因此,即使对于volatile,它是否明智也取决于特定的上下文-
相等和不相等的值的混合,周围的代码等等。通常我不会在单线程情况下仅针对volatile进行此操作,除非我怀疑大量的集合是多余的。但是,在高度多线程的结构中,读取然后进行易失性写(或其他昂贵的操作,如CAS)是一种最佳做法,您会看到它是诸如

java.util.concurrent
结构之类的高质量代码。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/617190.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号