Java 多线程减少NUMA上的套接字吞吐量

Java 多线程减少NUMA上的套接字吞吐量,java,linux,multithreading,sockets,numa,Java,Linux,Multithreading,Sockets,Numa,我在16核NUMA机器上用RedHadLinux测试了一个Java程序。我测量了Java DatagramSocket(用于UDP)的吞吐量,即它每秒能够接收和发送多少数据包(大小为64字节)。该程序由一个套接字和在套接字上侦听的n个线程组成。当一个数据包到达时,他们会将有效负载复制到一个byte[]数组中,用该数组创建一个新的DatagramPacket,并将其直接发送回它的来源。可以将其视为UDP层上的ping 我发现Java DatagramSocket套接字在使用多个线程(即两个或四个)

我在16核NUMA机器上用RedHadLinux测试了一个Java程序。我测量了Java DatagramSocket(用于UDP)的吞吐量,即它每秒能够接收和发送多少数据包(大小为64字节)。该程序由一个套接字和在套接字上侦听的n个线程组成。当一个数据包到达时,他们会将有效负载复制到一个byte[]数组中,用该数组创建一个新的DatagramPacket,并将其直接发送回它的来源。可以将其视为UDP层上的ping

  • 我发现Java DatagramSocket套接字在使用多个线程(即两个或四个)时实现的吞吐量要小得多。如果我只使用一个线程来侦听套接字,那么我可以实现每秒122000个数据包的吞吐量,而多个线程只能实现每秒65000个数据包。现在,我了解到一个线程可能会在NUMA机器的任何核心上执行,如果内存必须从一个节点移动到另一个节点,那么内存访问就会变得昂贵。但是,如果我有两个线程,那么只有一个线程应该在“错误”的内核上执行,而另一个线程仍然应该实现非常高的吞吐量。另一种可能的解释是Datagramsocket中存在同步问题,但这些只是猜测。有人对真正的解释有很好的洞察力吗

  • 我发现在多个端口上多次(并行)执行该程序可以获得更高的总体吞吐量。我用一个线程启动程序四次,每个程序在一个单独的端口(5683、5684、5685和5686)上使用一个套接字。这四个程序的总吞吐量为每秒370000个数据包。总之,在同一端口上使用多个线程会降低吞吐量,而在多个端口上使用一个线程会增加吞吐量。这是怎么解释的

  • 系统规格:

    硬件:2个AMD Opteron(TM)处理器6212个处理器上有16个内核,由4个节点组成,每个节点具有32 GB RAM。频率:1.4 Ghz,2048 KB缓存

    node distances:
    node   0   1   2   3
      0:  10  16  16  16
      1:  16  10  16  16
      2:  16  16  10  16
      3:  16  16  16  10
    

    该操作系统是Red Hat Enterprise Linux Workstation 6.4版(圣地亚哥),内核版本为
    2.6.32-358.14.1.el6.x86_64
    。Java版本
    “1.7.0_09”
    、Java(TM)SE运行时环境(
    构建1.7.0_09-b05
    )、Java热点(TM)64位服务器VM(
    构建23.5-b02,混合模式
    ),我使用了
    -XX:+UseNUMA
    标志。服务器和客户端通过10GB以太网连接。

    通常,仅使用一个线程时效率最高。使材料平行将不可避免地引入成本。只有当您并行完成的额外工作量超过此成本时,吞吐量才会增加

    现在,演示了吞吐量的理论增益,它与您的工作中有多少是由可并行化/不可并行化的内容组成有关。例如,如果您的任务只有50%是可并行的,那么无论您在这个问题上抛出了多少线程,您的吞吐量都只能增加2倍。请注意,您在链接中看到的图表忽略了添加线程的成本。实际上,本机操作系统线程确实会增加相当多的成本,尤其是当许多线程试图访问共享资源时


    在您的例子中,当您只使用一个套接字时,您的大部分工作是不可并行的。因此,使用单个线程可以提供更高的性能,而添加线程会使性能更差,因为它们增加了成本。在第二个实验中,您增加了可以通过使用多个套接字进行并行化的工作量。因此,尽管使用线程增加了一些成本,您还是获得了吞吐量。

    您应该从提供基准测试代码开始。我不明白您为什么要在非并行的东西(如网络)上测试并行性。尝试使用多个线程写入单个磁盘时会得到类似的结果。您能用4个不同的套接字测试4个线程吗?我的猜测是,它比4个线程/1个套接字要好得多,但可能比4个进程/4个套接字稍差。顺便说一句,
    DatagramSocket.receive()
    已同步。