内存屏障和 TLB
内存屏障保证数据缓存是一致的.但是,它是否保证 TLB 是一致的?
Memory barriers guarantee that the data cache will be consistent. However, does it guarantee that the TLB will be consistent?
我看到一个问题,即在线程之间传递 MappedByteBuffer 时,JVM(java 7 update 1)有时会因内存错误(SIGBUS、SIGSEG)而崩溃.
I am seeing a problem where the JVM (java 7 update 1) sometimes crashes with memory errors (SIGBUS, SIGSEG) when passing a MappedByteBuffer between threads.
例如
final AtomicReference<MappedByteBuffer> mbbQueue = new AtomicReference<>();
// in a background thread.
MappedByteBuffer map = raf.map(MapMode.READ_WRITE, offset, allocationSize);
Thread.yield();
while (!inQueue.compareAndSet(null, map));
// the main thread. (more than 10x faster than using map() in the same thread)
MappedByteBuffer mbb = inQueue.getAndSet(null);
如果没有 Thread.yield(),我偶尔会在 force()、put() 和 C 的 memcpy() 中崩溃,所有这些都表明我试图非法访问内存.使用 Thread.yield() 我没有遇到问题,但这听起来不是一个可靠的解决方案.
Without the Thread.yield() I occasionally get crashes in force(), put(), and C's memcpy() all indicating I am trying to access memory illegally. With the Thread.yield() I haven't had a problem, but that doesn't sound like a reliable solution.
有人遇到过这个问题吗?TLB 和内存屏障有什么保证吗?
Has anyone come across this problem? Are there any guarantees about TLB and memory barriers?
操作系统是 Centos 5.7,我已经看到了 i7 和 Dual Xeon 机器上的行为.
The OS is Centos 5.7, I have seen the behaviour on i7 and a Dual Xeon machines.
我为什么要这样做?因为写入消息的平均时间为 35-100 ns,具体取决于长度,并且使用普通的 write() 并没有那么快.如果我在当前线程中进行内存映射和清理,这需要 50-130 微秒,使用后台线程来完成主线程交换缓冲区大约需要 3-5 微秒.为什么我需要交换缓冲区?因为我正在写入许多 GB 的数据,而 ByteBuffer 的大小不能超过 2 GB.
Why do I do this? Because the average time to write a message is 35-100 ns depending on length and using a plain write() isn't as fast. If I memory map and clean up in the current thread this takes 50-130 microseconds, using a background thread to do it takes about 3-5 microseconds for the main thread to swap buffers. Why do I need to be swapping buffers at all? Because I am writing many GB of data and ByteBuffer cannot be 2+ GB in size.
推荐答案
映射是通过 mmap64 (FileChannel.map) 完成的.当访问该地址时,将出现页面错误,内核将在那里为您读/写.在 mmap 期间不需要更新 TLB.
The mapping is done via mmap64 (FileChannel.map). When the address is accessed there will be a page fault and the kernel shall read/write there for you. TLB doesn't need to be updated during mmap.
TLB(所有 CPU 的)在 munmap 期间未经验证,由 MappedByteBuffer 的最终确定处理,因此 munmap 成本高.
TLB (of all cpus) is unvalidated during munmap which is handled by the finalization of the MappedByteBuffer, hence munmap is costly.
映射涉及大量同步,因此地址值不会被破坏.
Mapping involves a lot synchronization so the address value shall not be corrupted.
你有没有机会通过 Unsafe 尝试花哨的东西?
Any chance you try fancy stuff via Unsafe?
相关文章