如果并行Java程序内存有限,如何提高性能?

如果并行Java程序内存有限,如何提高性能?,java,multithreading,optimization,parallel-processing,bandwidth,Java,Multithreading,Optimization,Parallel Processing,Bandwidth,我编写了一个并行java程序。它通常在以下情况下工作: 它以字符串输入作为输入; 然后将输入均匀地切割成字符串输入[numThreads]; 每个输入[i]被分配给线程_i进行处理,并生成结果[i]; 所有工作线程完成后,主线程将结果[i]合并到结果中。 10芯物理核心机器的性能数据如下所示 Threads# 1 thread 2 threads 4 threads 8 threads 10 threads Time(ms) 78 41

我编写了一个并行java程序。它通常在以下情况下工作:

它以字符串输入作为输入; 然后将输入均匀地切割成字符串输入[numThreads]; 每个输入[i]被分配给线程_i进行处理,并生成结果[i]; 所有工作线程完成后,主线程将结果[i]合并到结果中。 10芯物理核心机器的性能数据如下所示

Threads#    1 thread    2 threads   4 threads   8 threads   10 threads
Time(ms)       78           41          28          21           21
注:

JVM预热时间已在前50次运行中取消。 时间不包括线程开始/加入时间。 当线程数超过8个时,内存带宽似乎成为瓶颈

在这种情况下,如何进一步提高性能?我的并行Java程序中有任何设计问题吗

为了研究这个可伸缩性问题的原因,我在processinputs[I]方法中插入了一个无意义的计算循环。以下是新数据:

Threads#    1 thread      10 threads
Time(ms)     41000          4330
新数据显示10个线程具有良好的可伸缩性,这反过来确认了原始的无意义循环存在内存问题,因此其可伸缩性限制为8个线程


但是无论如何,为了避免这个问题,比如将数据预加载到每个核心的本地缓存中,或者批量加载?

我发现这里不太可能存在内存带宽问题。更有可能的是,您的运行时间很短,当您接近0时,您主要只是在计时线程启动/关闭或热交换编译器优化周期。从运行时间如此短的Java任务中获取相关的计时信息几乎毫无价值。最初运行的热插拔编译器和其他优化通常在类生命早期主导CPU使用。我们的生产应用程序只有在现场服务运行几分钟后才能稳定下来

如果您可以通过添加更多输入数据或反复计算相同的结果来显著增加运行时间,您可能会更好地了解最佳线程数

编辑:

现在,您已经在更长的时间内为1个线程和10个线程添加了计时,在我看来,您不受任何约束,因为计时似乎是相当线性的——有一些线程开销。41000/10=4100,而10个螺纹为4330


非常好地演示了线程可以对CPU受限的应用程序执行哪些操作:-

我发现这里不太可能有内存带宽问题。更有可能的是,您的运行时间很短,当您接近0时,您主要只是在计时线程启动/关闭或热交换编译器优化周期。从运行时间如此短的Java任务中获取相关的计时信息几乎毫无价值。最初运行的热插拔编译器和其他优化通常在类生命早期主导CPU使用。我们的生产应用程序只有在现场服务运行几分钟后才能稳定下来

如果您可以通过添加更多输入数据或反复计算相同的结果来显著增加运行时间,您可能会更好地了解最佳线程数

编辑:

现在,您已经在更长的时间内为1个线程和10个线程添加了计时,在我看来,您不受任何约束,因为计时似乎是相当线性的——有一些线程开销。41000/10=4100,而10个螺纹为4330


非常好地演示了线程可以对CPU受限的应用程序执行哪些操作:-

我想说你的损失是因为换了线程。您的线程比内核多,并且不需要为较慢的进程阻塞线程,因此它们被切换进来,做一些工作,然后gettimg被切换出去,以切换另一个进程。切换线程是一个昂贵的过程,考虑到您所做的事情的性质,我会本能地将线程数量限制为8,为操作系统保留两个内核,而您的性能数字似乎证明了这一点。

我想说,您的损失在于切换线程。您的线程比内核多,并且不需要为较慢的进程阻塞线程,因此它们被切换进来,做一些工作,然后gettimg被切换出去,以切换另一个进程。切换线程是一个昂贵的过程,考虑到你所做的事情的性质,我会本能地将线程数量限制为8,为操作系统保留两个内核,而你的性能数字似乎证明了这一点。

你有多少个逻辑内核

想象一下,你有一个内核和一百个线程。要做的工作是相同的,它不能分布在多个核心上,但现在您有大量的线程切换开销

现在假设你有四个内核和四个线程。假设没有其他瓶颈,计算时间是四分之一

现在假设您有四个内核和八个线程。您的计算时间大约是四分之一,但您会增加一些线程交换开销

注意超读和tha
它可能会帮助你,也可能会阻碍你,这取决于计算任务的性质。

你有多少个逻辑核

想象一下,你有一个内核和一百个线程。要做的工作是相同的,它不能分布在多个核心上,但现在您有大量的线程切换开销

现在假设你有四个内核和四个线程。假设没有其他瓶颈,计算时间是四分之一

现在假设您有四个内核和八个线程。您的计算时间大约是四分之一,但您会增加一些线程交换开销



注意超读,它可能会帮助你,也可能会阻碍你,这取决于计算任务的性质。

有趣的想法!所以我测量了线程的启动时间。启动5个线程大约需要1毫秒。启动10个线程大约需要3毫秒。我想得越多,就越有可能是热插拔编译器优化。你能通过将线程放入循环或@Jack之类的东西来[人为地]增加线程的运行时间吗?我测量了线程运行的时间。所以它不计算线程开始/结束的开销。在问题中用新数据更新。所以它看起来和线程数是非常线性的?这就是我看到的“杰克”吗?一些线程开销,但不多?有趣的想法!所以我测量了线程的启动时间。启动5个线程大约需要1毫秒。启动10个线程大约需要3毫秒。我想得越多,就越有可能是热插拔编译器优化。你能通过将线程放入循环或@Jack之类的东西来[人为地]增加线程的运行时间吗?我测量了线程运行的时间。所以它不计算线程开始/结束的开销。在问题中用新数据更新。所以它看起来和线程数是非常线性的?这就是我看到的“杰克”吗?一些线程开销,但不多?该程序在10核机器上运行。他们不需要经常切换。我真的怀疑上下文切换是否会如此昂贵@Tony。这些天来,甚至连一个JVM都很难搞定。@jackWM。与内核数较少的处理器相比,在相同负载下,它们需要较少的切换次数。如果线程需要有限的内存,并且它唯一需要的资源是一个要在其上执行的核心,而您所做的只是增加了对核心的需要,那么它要么鞭打线程,要么停止线程,直到它获得更多的空间。因此,创建比核心更多的线程是行不通的。是的,你是对的。这就是为什么我没有报告任何使用超过10个线程的情况。所以在我这里展示的所有例子中,核心已经足够了,不需要切换。程序运行在一台10核的机器上。他们不需要经常切换。我真的怀疑上下文切换是否会如此昂贵@Tony。这些天来,甚至连一个JVM都很难搞定。@jackWM。与内核数较少的处理器相比,在相同负载下,它们需要较少的切换次数。如果线程需要有限的内存,并且它唯一需要的资源是一个要在其上执行的核心,而您所做的只是增加了对核心的需要,那么它要么鞭打线程,要么停止线程,直到它获得更多的空间。因此,创建比核心更多的线程是行不通的。是的,你是对的。这就是为什么我没有报告任何使用超过10个线程的情况。所以,在我这里展示的所有案例中,核心已经足够了,并且不需要切换。您有意或无意地实现了。干得好也许有免费的分布式实现可以让你在更多的计算机上扩展它。我不理解@Jack。在我看来,你没有受到任何约束。41000/10==4100,接近4330以说明线程开销。那么它是如何确认内存问题的呢?@Gray我得到41000/4330的原因是我在进程方法中插入了一个额外的无用的计算循环。原来的程序没有它,加速会在8个线程时停止。那么8个线程@Jack的速度是多少呢?我仍然认为你对内存瓶颈的看法是错误的。除非您谈论的是对象/gc带宽,而不是内存访问带宽。我可以相信。@Gray你是说插入循环的程序?如果是这样,它应该有线性加速,比如5500毫秒。我说的是内存带宽。如果你认为我是错的,你能再解释一下原因吗?你是有意还是无意实施的。干得好也许有免费的分布式实现可以让你在更多的计算机上扩展它。我不理解@Jack。在我看来,你没有受到任何约束。41000/10==4100,接近4330以说明线程开销。那么它是如何确认内存问题的呢?@Gray我得到41000/4330的原因是我在进程方法中插入了一个额外的无用的计算循环。原来的程序没有它,加速会在8个线程时停止。那么8个线程@Ja的速度是多少
ck?我仍然认为你对内存瓶颈的看法是错误的。除非您谈论的是对象/gc带宽,而不是内存访问带宽。我可以相信。@Gray你是说插入循环的程序?如果是这样,它应该有线性加速,比如5500毫秒。我说的是内存带宽。如果你认为我错了,你能再解释一下原因吗?