【问题标题】:efficiently growing atomic arrays有效增长的原子阵列
【发布时间】:2016-04-27 19:27:50
【问题描述】:

是否有任何干净有效的方法来发展Atomic(Double|Integer|Long|Reference)Array?在我的特殊情况下,复制期间没有并发写入。一种创建新数组并将源数组循环复制到目标数组的明显方法:

Atomic*Array dest = new Atomic*Array(newSize);
for (int i = 0, len = src.length(); i < len; ++i)
    dest.set(i, src.get(i));

这段代码可能比System.arraycopyArrays.copy 类型的函数慢得多,而且有点冗长。

我可以交替使用反射来获取源的 array 字段,扩展它,并将新数组传递给 Atomic*Array 构造函数:

try {
    Field arrayField = Atomic*Array.class.getDeclaredField("array");
    arrayField.setAccessible(true);
    ArrayType srcArray = (ArrayType)arrayField.get(src);
    Atomic*Array dest = new Atomic*Array(Arrays.copyOf(srcArray, newLength));
} catch (IllegalAccessException | NoSuchFieldException e) {
    throw new AssertionError(e);
}

但是这段代码也有问题:它依赖于Atomic*Array类的实现,对数组进行了两份拷贝——一份在Arrays.copy中,一份在dest的构造函数中,非常难看。

有没有更快/更清洁的方法来做到这一点?

【问题讨论】:

  • 您可以通过从旧数组链接到新数组来增长,以便消费者可以遍历。有关此想法的示例,请参见 JCTools 的 MpscChunkedArrayQueue
  • @BenManes 是的,我想到了这个想法,但是内存使用量要高得多,因为我们需要将数组包装在链接中,并且逻辑有点复杂,因为我们需要维护一个近似的尾指针。就像单个数组一样工作正常,但我希望从这种方法中获得尽可能多的性能。
  • @Solomonoff'sSecret 假设一个对象数组,你不会在最后一个索引中存储转发链接吗?由于擦除,丑陋的演员阵容值得性能提升。大多数情况下,您将使用正确的尺寸,并且只在增长时支付罚金。您的另一个选择是在 VarHandles 到达之前使用 Unsafe。
  • @BenManes 没错,您可以将下一个链接存储在最后一个索引中。这是个好主意。对,擦除意味着无论如何都会发生强制转换。

标签: java performance concurrency java.util.concurrent


【解决方案1】:

我担心我会让你失望:没有办法批量复制Atomic*Array

看看javadoc,没有一种方法可以一次给你一个以上的元素。即使使用您可以传递给 AtomicIntegerArray 构造函数的原始数组也不会有所帮助 - 该类在内部进行复制。

查看AtomicIntegerArray 源代码,使用反射可以工作——该类对array 字段没有任何特殊作用。不过,我不会。它不仅安全,而且反射也会给你一些你试图避免的性能损失。

如果您的目标是扩大阵列,您可能需要查看LinkedBlockingQueueCopyOnWriteArrayList,具体取决于您的用例。

我遇到过几次这个问题,最后我们总能找到一种没有Atomic*Array 的方法。

【讨论】:

  • 谢谢。我的目标是实现一个非常节省内存的、非阻塞的仅附加列表。但不幸的是,LinkedBlockingQueueCopyOnWriteArrayList 都不能满足我的需求:前者为其节点使用了太多内存,而后者效率极低。事实上,标准库中没有任何集合符合我的要求。
  • 很遗憾你添加了“非阻塞”要求,否则你也可以用旧的synchronized :)
  • 对,Collections.synchronizedList(new ArrayList&lt;&gt;()) 可以工作,但非阻塞更可取,因为绝大多数时间没有争用,因此速度明显更快(即使通过循环复制来增长:)) .
  • 两者都有。集合可能会变得非常大,因此具有二次性能的写入时复制将非常缓慢。此外,该策略不允许并行写入,这是一个问题。顺便说一句,我的实现与ConcurrentLinkedQueue 的执行速度大致相同,并且内存效率显着提高。 (另一方面,它只支持追加和读取。)
猜你喜欢
  • 2019-01-20
  • 1970-01-01
  • 2016-05-04
  • 2022-01-14
  • 2017-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多