2 个 JVM 之间的低 CPU 使用率轮询架构

Low CPU usage polling architecture between 2 JVMs

服务器环境

关于申请:

架构:

问题

问题:

是否有更复杂的方法来保持轮询内存映射文件中的值,这涉及最小的开销、最小的延迟和最小的 CPU 利用率?请注意,每微秒延迟都会降低性能

代码段

模块 B 的代码片段(轮询和读取内存映射文件的无限循环)如下

FileChannel fc_pointer = new RandomAccessFile(file, "rw").getChannel();
      MappedByteBuffer mem_file_pointer =fc_pointer.map(FileChannel.MapMode.READ_ONLY, 0, bufferSize);
      long address_file_pointer = ((DirectBuffer) mem_file_pointer).address();


    while(true)
    {
        int value_from_memory_mapped_file = unsafe.getInt(address_file_pointer);

        if (value_from_memory_mapped_file .. is different from the last read value)
        {
            //do some operation.... 
        //exit the routine;
        }
        else
        {
            continue;
        }
}//end of while
  1. 高负载 CPU 是尽可能低延迟的实际成本。在使用无锁信号的实用架构中,每个 CPU 套接字应该 运行 不超过一对消费者-生产者线程。一对吃一个或两个(如果没有固定到启用超线程的单个 Intel CPU 内核,则每个线程一个内核)内核几乎完全吃掉(这就是为什么在大多数情况下,您在构建超-许多客户端的低延迟服务器系统)。顺便说一句,在性能测试和禁用电源管理之前,不要忘记使用 "taskset" 将每个进程固定到特定内核。

  2. 当您在长时间旋转无结果后锁定消费者时,有一个众所周知的技巧。但是您必须花一些时间来停放然后取消停放线程。当然,这是零星延迟增加的时刻,但是当线程停放时 CPU 核心是空闲的。参见,例如:http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf(8.4.4 更长周期的同步) 此外,可以在此处找到 java 的不同等待策略的很好说明: https://github.com/LMAX-Exchange/disruptor/wiki/Getting-Started(替代等待策略)

  3. 如果您谈论的是毫秒 (ms),而不是微秒 (µs),您可以尝试通过环回进行 TCP 套接字通信。它增加了大约 10 微秒来将少量数据从生产者传递到消费者,这是阻塞技术。命名管道比套接字具有更好的延迟特性,但它们实际上是非阻塞的,您必须再次构建类似自旋循环的东西。内存映射文件 + 内在 Unsafe.getXXX(这是一个 x86 MOV)在延迟和吞吐量方面仍然是最好的 IPC 技术,因为它在读写时不需要系统调用。

  4. 如果您仍打算使用无锁和内存映射文件以及使用不安全的直接访问,请不要忘记为生产者和消费者设置适当的内存屏障。例如,"unsafe.getIntVolatile" 而不是第一个 "unsafe.getInt" 如果您不确定您的代码总是 运行s 在以后的 x86 上。

  5. 如果您看到意外的 CPU 利用率,每对生产者-消费者不应超过 30-40%(6 个核心使用 2 个核心 CPU),您必须使用标准工具来检查其他内核上的 运行ning 和整体系统性能。如果您看到与映射文件关联的密集 IO,请确保它已映射到 tmpfs 文件系统以防止真正的磁盘 IO。检查 "fattest" 进程的内存总线加载和 L3 缓存未命中,因为我们知道,CPU 时间 = (CPU exec 时钟周期 + _memory_stall_cycles_) * 时钟周期时间

最后,一个非常相似且有趣的开源项目,其中包含一个如何使用内存映射文件的好例子:http://openhft.net/products/chronicle-queue/