这是一个一般的编程问题。让我们说我有一个线程在进行特定的模拟,速度非常重要。在每次迭代中,我都想从中提取数据并将其写入文件。
将数据移交给不同的线程并让模拟线程专注于他的工作是一种更好的做法,或者由于速度非常重要,使模拟线程也可以进行数据记录而无需复制数据。 (在我的例子中,它是3-5个整数,大小为1000-10000)
首先,它肯定取决于我们正在复制多少数据,但它还能依赖什么?同步和复制的成本是否值得?在每次迭代中创建小的runnables以处理每秒50次或更多迭代的记录任务是一个好习惯吗?
答案 0 :(得分:3)
如果您确实希望在此静态捕获中获得低延迟,并且在模拟过程中需要它,那么我会想到两种技术。它们可以非常有效地一起使用。请注意,这两种方法与标准的Java trodden路径相距甚远,因此请先测量并确认在滥用它们之前需要这些技术;它们很难正确实施。
在模拟过程中将数据写入文件的最快方法是在不减慢模拟速度的情况下将工作交给另一个线程。然而,必须注意如何发生切换,因为模拟线程中的内存屏障将减慢模拟。鉴于作者只关心值最终会出现,我会考虑使用位于AtomicLong.lazySet后面的内存屏障,它会请求线程安全写入内存地址而不会阻塞,以使写入实际上对另一个线程可见。不幸的是,目前只能通过lazySet或类sun.misc.Unsafe直接访问此内存屏障,这显然不是公共Java API的一部分。然而,这不应该是一个障碍,因为它在所有当前的JVM实现上都是如此,而Doug Lea正在谈论将其部分转移到主流中。
避免Java使用的缓慢阻塞文件IO;使用内存映射文件。这使操作系统可以代表您为您执行异步IO,并且非常高效。它还支持使用上面提到的相同内存屏障。
对于这两种技术的例子,我强烈建议阅读Peter Lawrey的HFT Chronicle的源代码。事实上,HFT Chronicle可能只是你在这里使用的库。它提供了一个高效且易于使用的磁盘支持队列,每秒可以维持大约一百万条消息。
答案 1 :(得分:3)
在我对stress-testing HTTP client的工作中,我将统计数据存储到一个数组中,当阵列准备好发送到GUI时,我会为测试人员客户端创建一个新数组,并将整个数组交给网络层。这意味着您不需要为任何复制付费,仅用于分配新阵列(JVM上的超快速操作,涉及手工编码的汇编程序宏以利用可用于该任务的最佳SIMD指令)。
我还建议不要把自己置身于最佳内存屏障使用领域;普通volatile
写和AtomicReference.lazySet()
之间的差异只能是可测量的,如果你的线程除了执行内存屏障(每秒至少数百万次写入)之外什么都不做。根据您的目标I / O吞吐量,您甚至可能不需要NIO来实现目标。最好先使用简单,易于维护的代码,而不是深入研究高度专业化的API,而无需确认需要。