Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/309.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在基准测试程序中使用过多线程的问题_Java_Multithreading_Benchmarking_Multicore - Fatal编程技术网

Java 在基准测试程序中使用过多线程的问题

Java 在基准测试程序中使用过多线程的问题,java,multithreading,benchmarking,multicore,Java,Multithreading,Benchmarking,Multicore,我已经用Java编写了一个非常简单的基准测试。它只是将一个双精度值增加到一个指定的值,并花费时间 当我在我的6核桌面上使用单线程或线程数不超过100个时,基准测试返回合理且可重复的结果 但当我使用1200个线程时,平均多核持续时间比单核持续时间低10倍或更多。我已经确保无论使用多少线程,增量的总量都是相同的 为什么更多线程会导致性能下降这么多?有办法解决这个问题吗 我正在发布我的消息来源,但我不认为有问题 Benchmark.java: package sibbo.benchmark; imp

我已经用Java编写了一个非常简单的基准测试。它只是将一个双精度值增加到一个指定的值,并花费时间

当我在我的6核桌面上使用单线程或线程数不超过100个时,基准测试返回合理且可重复的结果

但当我使用1200个线程时,平均多核持续时间比单核持续时间低10倍或更多。我已经确保无论使用多少线程,增量的总量都是相同的

为什么更多线程会导致性能下降这么多?有办法解决这个问题吗

我正在发布我的消息来源,但我不认为有问题

Benchmark.java:

package sibbo.benchmark;

import java.text.DecimalFormat;
import java.util.LinkedList;
import java.util.List;

public class Benchmark implements TestFinishedListener {
            private static final double TARGET = 1e10;
    private static final int THREAD_MULTIPLICATOR = 2;

    public static void main(String[] args) throws InterruptedException {
        Benchmark b = new Benchmark(TARGET);
        b.start();
    }

    private int coreCount;
    private List<Worker> workers = new LinkedList<>();
    private List<Worker> finishedWorkers = new LinkedList<>();
    private double target;

    public Benchmark(double target) {
        this.target = target;
        getSystemInfos();
        printInfos();
    }

    private void getSystemInfos() {
        coreCount = Runtime.getRuntime().availableProcessors();
    }

    private void printInfos() {
        System.out.println("Usable cores: " + coreCount);
        System.out.println("Multicore threads: " + coreCount *                 THREAD_MULTIPLICATOR);
        System.out.println("Loops per core: " + new DecimalFormat("###,###,###,###,##0").format(TARGET));

        System.out.println();
    }

    public synchronized void start() throws InterruptedException {
        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

        System.out.print("Initializing singlecore benchmark... ");
        Worker w = new Worker(this, 0);
        workers.add(w);

        Thread.sleep(1000);
        System.out.println("finished");

        System.out.print("Running singlecore benchmark... ");
        w.runBenchmark(target);
        wait();

        System.out.println("finished");
        printResult();

        System.out.println();
        // Multicore
        System.out.print("Initializing multicore benchmark...  ");
        finishedWorkers.clear();

        for (int i = 0; i < coreCount * THREAD_MULTIPLICATOR; i++) {
            workers.add(new Worker(this, i));
        }

        Thread.sleep(1000);
        System.out.println("finished");

        System.out.print("Running multicore benchmark...  ");

        for (Worker worker : workers) {
            worker.runBenchmark(target / THREAD_MULTIPLICATOR);
        }

        wait();

        System.out.println("finished");
        printResult();

        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
    }

    private void printResult() {
        DecimalFormat df = new DecimalFormat("###,###,###,##0.000");

        long min = -1, av = 0, max = -1;
        int threadCount = 0;
        boolean once = true;

        System.out.println("Result:");

        for (Worker w : finishedWorkers) {
            if (once) {
                once = false;

                min = w.getTime();
                max = w.getTime();
            }

            if (w.getTime() > max) {
                max = w.getTime();
            }

            if (w.getTime() < min) {
                min = w.getTime();
            }

            threadCount++;
            av += w.getTime();

            if (finishedWorkers.size() <= 6) {
                System.out.println("Worker " + w.getId() + ": " + df.format(w.getTime() / 1e9) + "s");
            }
        }

        System.out.println("Min: " + df.format(min / 1e9) + "s, Max: " + df.format(max / 1e9) + "s, Av per Thread: "
                + df.format((double) av / threadCount / 1e9) + "s");
    }

    @Override
    public synchronized void testFinished(Worker w) {
        workers.remove(w);
        finishedWorkers.add(w);

        if (workers.isEmpty()) {
            notify();
        }
    }
}
Worker.java:

package sibbo.benchmark;

public class Worker implements Runnable {
    private double value = 0;
    private long time;
    private double target;
    private TestFinishedListener l;
    private final int id;

    public Worker(TestFinishedListener l, int id) {
        this.l = l;
        this.id = id;

        new Thread(this).start();
    }

    public int getId() {
        return id;
    }

    public synchronized void runBenchmark(double target) {
        this.target = target;
        notify();
    }

    public long getTime() {
        return time;
    }

    @Override
    public void run() {
        synWait();
        value = 0;
        long startTime = System.nanoTime();

        while (value < target) {
            value++;
        }

        long endTime = System.nanoTime();
        time = endTime - startTime;

        l.testFinished(this);
    }

    private synchronized void synWait() {
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

您需要了解,OS或Java线程调度器(或两者)正在尝试平衡应用程序中的所有线程,以使它们都有机会执行某些工作,并且在线程之间切换的成本是非零的。对于1200个线程,您刚刚达到并且可能远远超过了临界点,处理器在切换上下文方面花费的时间比实际工作要多

这里有一个粗略的类比:

你在A房间有一项工作要做。你每天在A房间里站8个小时,完成你的工作

然后你的老板过来告诉你,你也必须在B房间里工作。现在你需要定期离开A房间,沿着大厅走到B房间,然后走回去。每天步行需要1分钟。现在你要花3个小时,59.5分钟完成每项工作,一分钟在房间之间走动

现在想象一下,你有1200个房间可以工作。你将花更多的时间在房间之间走动,而不是做实际工作。这就是您将处理器放入的情况。它花了太多时间在不同的环境之间切换,以至于没有真正的工作完成


编辑:现在,根据下面的评论,在继续之前,您可能会在每个房间花费固定的时间-您的工作会继续,但房间之间的上下文切换数量仍然会影响单个任务的总体运行时间。

您需要了解操作系统或Java线程调度程序,或者两者都试图在应用程序中的所有线程之间取得平衡,以使它们都有机会执行某些工作,并且在线程之间切换的成本是非零的。对于1200个线程,您刚刚达到并且可能远远超过了临界点,处理器在切换上下文方面花费的时间比实际工作要多

这里有一个粗略的类比:

你在A房间有一项工作要做。你每天在A房间里站8个小时,完成你的工作

然后你的老板过来告诉你,你也必须在B房间里工作。现在你需要定期离开A房间,沿着大厅走到B房间,然后走回去。每天步行需要1分钟。现在你要花3个小时,59.5分钟完成每项工作,一分钟在房间之间走动

现在想象一下,你有1200个房间可以工作。你将花更多的时间在房间之间走动,而不是做实际工作。这就是您将处理器放入的情况。它花了太多时间在不同的环境之间切换,以至于没有真正的工作完成


编辑:现在,根据下面的评论,在继续之前,也许你在每个房间里花了固定的时间-你的工作会进展,但是房间之间的上下文切换数量仍然会影响单个任务的整体运行时间。

好的,我想我已经发现了我的问题,但直到现在,还没有解决方案

当测量每个线程执行其部分工作的时间时,对于不同的线程总量,可能存在不同的最小值。最大值每次都相同。如果线程首先启动,然后经常暂停,最后完成。例如,该最大值可能为10秒。假设每个线程完成的操作总量保持不变,无论我使用多少线程,当使用不同数量的线程时,单个线程完成的操作量都必须改变。例如,使用一个线程,它必须执行1000个操作,但使用十个线程,每个线程只需执行100个操作。现在,使用十个线程,一个线程可以使用的最短时间比使用一个线程要少得多。因此,计算每个线程完成工作所需的平均时间是毫无意义的。使用十个线程的最小时间为1秒。如果一个线程不间断地工作,就会发生这种情况

编辑


解决方法是简单地测量从第一个线程开始到最后一个线程完成之间的时间量。

好的,我想我已经发现了我的问题,但直到现在,还没有解决方案

当测量每个线程执行其部分工作的时间时,有不同的 t不同总螺纹数的可能最小值。最大值每次都相同。如果线程首先启动,然后经常暂停,最后完成。例如,该最大值可能为10秒。假设每个线程完成的操作总量保持不变,无论我使用多少线程,当使用不同数量的线程时,单个线程完成的操作量都必须改变。例如,使用一个线程,它必须执行1000个操作,但使用十个线程,每个线程只需执行100个操作。现在,使用十个线程,一个线程可以使用的最短时间比使用一个线程要少得多。因此,计算每个线程完成工作所需的平均时间是毫无意义的。使用十个线程的最小时间为1秒。如果一个线程不间断地工作,就会发生这种情况

编辑



解决方案是简单地测量从第一个线程开始到最后一个线程完成之间的时间量。

我假设您要求解释为什么它会这样运行?当您运行1200个线程时,是否有可能内存不足?您能用jconsole查看您的应用程序,看看内存图是否显示完整的伊甸园和幸存者空间吗?您可能需要增加-XmxYes的可用内存,这正是我感兴趣的。我从未使用过jconsole,因此我将首先将最大内存量设置为更高的值。需要意识到的重要一点是,每个线程将消耗固定数量的堆栈空间内存。IIRC,512k,但可通过-Xss配置。因此,无论线程在做什么工作,大量线程都会消耗大量内存。我假设您要求解释为什么它会这样运行?当您运行1200个线程时,是否有可能内存不足?您能用jconsole查看您的应用程序,看看内存图是否显示完整的伊甸园和幸存者空间吗?您可能需要增加-XmxYes的可用内存,这正是我感兴趣的。我从未使用过jconsole,因此我将首先将最大内存量设置为更高的值。需要意识到的重要一点是,每个线程将消耗固定数量的堆栈空间内存。IIRC,512k,但可通过-Xss配置。因此,无论线程在做什么工作,大量线程都将消耗大量内存贪婪-有足够的线程,盒子在它们之间跳转的时间比实际运行它们的时间要多。这并不能解释为什么单核版本受过多的threads@Evgeni我假设OP在这里误用了术语single core来表示单线程,这是基于他在代码中使用的术语。不,不会,或者至少,不应该。一旦操作系统的就绪线程数量是内核数量的两倍以上,上下文切换的速度就再高不过了。4核机器上有8个或800个准备就绪的CPU密集型线程并不重要——在每次中断时,4个线程将被抢占,并由优先级集准备就绪队列头部的4个其他线程替换,将oold线程推到尾部。唯一可能出现的问题是,如果创建的线程堆栈大小不合适,那么线程堆栈可能存在内存交换问题。@Chrishain在更仔细地检查了他的代码后同意了这一点。很抱歉同意-有了足够的线程,盒子在它们之间跳跃的时间将比实际运行它们的时间要长。这并不能解释为什么单核版本受到的线程数量过多的影响较小threads@Evgeni根据OP在代码中使用的术语,我假设他在这里误用了术语“单核”来表示“单线程”。不,它不会,或者至少不应该。一旦操作系统的就绪线程数量是内核数量的两倍以上,上下文切换的速度就再高不过了。4核机器上有8个或800个准备就绪的CPU密集型线程并不重要——在每次中断时,4个线程将被抢占,并由优先级集准备就绪队列头部的4个其他线程替换,将oold线程推到尾部。唯一可能出现的问题是,如果创建的线程堆栈大小不合适,那么线程堆栈可能存在内存交换问题。@Chrishain在更仔细地检查了他的代码后同意了这一点。很抱歉每个核心的线程越多,每个线程完成所需的时间就越长。当你拥有比内核更多的mroe线程时,即使是端到端的时间也应该增加。每个内核的线程越多,每个线程完成所需的时间就越长。一旦mroe线程比内核多,即使端到端时间也应该增加。