多线程访问内存 - java

我正在编写一个在Nehalem处理器上运行的多线程Java应用程序。但是我有一个问题,从4个线程开始,我几乎看不到应用程序的加速。

我做了一些简单的测试。我创建了一个线程,该线程仅分配一个大数组并可以访问该数组中的随机条目。因此,当我运行线程数时,运行时间不应更改(假设我没有超过可用的CPU内核数)。但是我观察到的是,运行1或2个线程几乎需要花费相同的时间,但是运行4或8个线程则要慢得多。因此,在尝试解决应用程序中的算法和同步问题之前,我想弄清楚我可以实现的最大并行化能力。

我使用了-XX:+UseNUMA JVM选项,因此应该在对应线程附近的内存中分配数组。

附言如果线程进行简单的数学计算,则4个甚至8个线程都不会浪费时间,因此我得出结论,当线程访问内存时,我会遇到一些问题。

任何帮助或想法表示赞赏,谢谢。

编辑

谢谢大家的答复。我发现我对自己的解释还不够好。

在尝试消除应用程序中的同步问题之前,我做了一个简单的测试,检查可以实现的最佳并行化。代码如下:

public class TestMultiThreadingArrayAccess {
    private final static int arrSize = 40000000;

    private class SimpleLoop extends Thread {
        public void run() {
            int array[] = new int[arrSize];
            for (long i = 0; i < arrSize * 10; i++) {
                array[(int) ((i * i) % arrSize)]++; // randomize a bit the access to the array
            }
            long sum = 0;
            for (int i = 0; i < arrSize; i++)
                sum += array[i];
        }
    }

    public static void main(String[] args) {
        TestMultiThreadingArrayAccess test = new TestMultiThreadingArrayAccess();
        for (int threadsNumber : new int[] { 1, 2, 4, 8 }) {
            Statistics timer = new Statistics("Executing " + threadsNumber+ " threads"); // Statistics is a simple helper class that measures the times
            timer.start();
            test.doTest(threadsNumber);
            timer.stop();
            System.out.println(timer.toString());
        }
    }

    public void doTest(int threadsNumber) {
        Thread threads[] = new Thread[threadsNumber];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new SimpleLoop();
            threads[i].start();
        }

        for (int i = 0; i < threads.length; i++)
            try {
                threads[i].join();
            } catch (InterruptedException e) {
            };
    }
}

因此,如您所见,在此最小测试中根本没有同步,而且数组的分配位于线程内部,因此应将其放置在可以快速访问的内存块中。此代码中也没有内存争用。对于4个线程,运行时间仍然减少了30%,而8个线程的运行速度却慢了两倍。正如您在代码中一样,我只是等待所有线程完成其工作,并且由于它们的工作是独立的,因此线程数不应该影响执行所花费的总时间。

在该计算机上,安装了2个四核超线程Nehalem处理器(总共16个CPU),因此,如果每个线程有8个线程,则只能捕获其CPU。

当我尝试使用较小的数组(20K条目)运行此测试时,4个线程的执行时间下降了7%,而8个线程的执行时间下降了14%,这是令人满意的。但是,当我尝试对大型数组(4000万个条目)进行随机访问时,运行时间急剧增加,因此我认为存在一个问题,即大块内存(因为它们不适合高速缓存?高效的方法。

有什么办法解决这个问题吗?

希望这能更好地阐明问题,再次感谢。

参考方案

测试的瓶颈是CPU到内存的带宽。即使本地内存可用,它也会被一定数量的线程共享。 (内存是节点的本地内存,而不是特定的核心内存。)一旦CPU可以轻易超过上述测试之类的简单循环的可用带宽,因此在这种测试中增加线程数不会提高性能,并且可能使性能恶化由于恶化的缓存一致性。

只是一项健全性测试,您是否还在使用并行收集器? -XX:+UseParallelGC。 UseNUMA仅在之后生效。

Java:正则表达式模式匹配器是否有大小限制? - java

我的模式类似于OR:“word1 | word2 | word3”我大约有800个字。可能有问题吗? 参考方案 您仅受记忆和理智的限制。 :)

Java:线程池如何将线程映射到可运行对象 - java

试图绕过Java并发问题,并且很难理解线程池,线程以及它们正在执行的可运行“任务”之间的关系。如果我创建一个有10个线程的线程池,那么我是否必须将相同的任务传递给池中的每个线程,或者池化的线程实际上只是与任务无关的“工人无人机”可用于执行任何任务?无论哪种方式,Executor / ExecutorService如何将正确的任务分配给正确的线程? 参考方案 …

JAVA:字节码和二进制有什么区别? - java

java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

Java:BigInteger,如何通过OutputStream编写它 - java

我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…