【问题标题】:Multithreading with Java / Benchmarking against MonoJava 多线程 / Mono 基准测试
【发布时间】:2012-10-22 05:19:38
【问题描述】:

我正在做一个简单的基准测试,在几个 Linux 机器上测试 Mono 的 ParallelFX 和 Java。 .NET 的测试在 Windows 和 Linux 上运行良好,但我在使用 Java 版本时遇到了一些问题......

我可以看到指定数量的线程正在启​​动,但它们以一种奇怪的方式运行。就像他们开始了,但他们完成得很慢。他们继续开始,但需要永远完成。似乎它应该超过线程池的限制,而我的 CPU 使用率在我看来似乎只使用了一个或两个内核(我有一个 i7 处理器,所以应该尝试使用 8 之类的东西)。

是的,我知道我的整数和其他东西可能不是“线程安全的”。我现在真的不在乎。这里有更大的问题。

C#版本

public class Program
{
    static void Main(string[] args)
    {
        const int numberOfCycles = 1000;
        const int numbersPerCycle = 1000000;

        Stopwatch swG = Stopwatch.StartNew();

        int threadCount = 0;
        int completeCount = 0;
        Parallel.For(0, numberOfCycles, x =>
            {
                Console.WriteLine(string.Format("Starting cycle {0}. Thread count at {1}", x, threadCount++));

                Random r = new Random();
                Stopwatch sw = Stopwatch.StartNew();
                List<double> numbers = new List<double>();
                for (int i = 0; i < numbersPerCycle; i++)
                {
                    numbers.Add(r.NextDouble() * 1000);
                }
                numbers.Sort();
                double min = numbers.Min();
                double max = numbers.Max();

                completeCount++;
                Console.WriteLine(string.Format("{0} cycles complete: {1:#,##0.0} ms. Min: {2:0.###}  Max: {3:0.###}", completeCount, sw.ElapsedMilliseconds, min, max));
                threadCount--;
            });

        Console.WriteLine(string.Format("All {0} cycles complete. Took {1:#,##0.0} ms.", numberOfCycles, swG.ElapsedMilliseconds));
        Console.WriteLine("Press any key to continue.");
        Console.ReadKey();
    }
}

Java 版本

附:我很懒,从这里偷了秒表类:Is there a stopwatch in Java?

public class JavaMonoTest {

    static int threadCount = 0;
    static int completeCount = 0;
    static String CLRF = "\r\n";

    public static void main(String[] args) throws IOException, InterruptedException {
        final int numberOfCycles = 1000;
        final int numbersPerCycle = 1000000;
        final int NUM_CORES = Runtime.getRuntime().availableProcessors();

        //Setup the running array
        List<Integer> cyclesList = new LinkedList<Integer>();
        for(int i = 0; i < numberOfCycles; i++){
            cyclesList.add(i);
        }

        Stopwatch swG = new Stopwatch();
        swG.start();

       ExecutorService exec = Executors.newFixedThreadPool(NUM_CORES);
       try {
           for (final Integer x : cyclesList) {
               exec.submit(new Runnable() {
                   @Override
                   public void run() {
                       System.out.printf("Starting cycle %s. Thread count at %s %s", x, threadCount++, CLRF);

                       Random r = new Random();
                       Stopwatch sw = new Stopwatch();
                       sw.start();
                       List<Double> numbers = new LinkedList<Double>();
                       for (int i = 0; i < numbersPerCycle; i++)
                       {
                           numbers.add(r.nextDouble() * 1000);
                       }
                       Collections.sort(numbers);
                       double min = Collections.min(numbers);
                       double max = Collections.max(numbers);

                       completeCount++;
                       System.out.printf("%s cycles complete: %.2f ms. Min: %.2f  Max: %.2f %s", completeCount, sw.getElapsedTime(), min, max, CLRF);
                       threadCount--;
                   }
               });
           }
       } finally {
           exec.shutdown();
       }
       exec.awaitTermination(1, TimeUnit.DAYS);

        System.out.printf("All %s cycles complete. Took %.2f ms. %s", numberOfCycles, swG.getElapsedTime(), CLRF);
        System.out.println("Press any key to continue.");
        System.in.read();
    }
}

更新了 C# 版本以匹配答案中的 Java 版本

public class Program
{
    static void Main(string[] args)
    {
        const int numberOfCycles = 1000;
        const int numbersPerCycle = 1000000;

        Stopwatch swG = Stopwatch.StartNew();

        int threadCount = 0;
        int completeCount = 0;
        Parallel.For(0, numberOfCycles, x =>
            {
                Console.WriteLine(string.Format("Starting cycle {0}. Thread count at {1}", x, Interlocked.Increment(ref threadCount)));

                Random r = new Random();
                Stopwatch sw = Stopwatch.StartNew();
                double[] numbers = new double[numbersPerCycle];
                for (int i = 0; i < numbersPerCycle; i++)
                {
                    numbers[i] = r.NextDouble() * 1000;
                }
                Array.Sort(numbers);
                double min = numbers[0];
                double max = numbers[numbers.Length - 1];

                Interlocked.Increment(ref completeCount);
                Console.WriteLine(string.Format("{0} cycles complete: {1:#,##0.0} ms. Min: {2:0.###}  Max: {3:0.###}", completeCount, sw.ElapsedMilliseconds, min, max));
                Interlocked.Decrement(ref threadCount);
            });

        Console.WriteLine(string.Format("All {0} cycles complete. Took {1:#,##0.0} ms.", numberOfCycles, swG.ElapsedMilliseconds));
        Console.WriteLine("Press any key to continue.");
        Console.ReadKey();
    }
}

【问题讨论】:

  • 您是否考虑过尝试在 Mono 下使用 IKVM.net 运行 java 代码?我很好奇自己在这种情况下比较这两者。
  • 我没有考虑过这样做。当您可以相当容易地获得本机 JVM 时,这似乎是额外的障碍?如果您确实尝试过,请告诉我们您的发现!
  • 对不起。非常努力,但无法让它在 Mono 下运行。我实际上一直在用我的代码这样做。我有一个关卡编辑器,它使用了我的游戏引擎中的一些类。引擎是 Java Android,编辑器是在 MonoDevelop 中使用 GTK# 制作的。 ataquedascuruja.files.wordpress.com/2012/06/…

标签: c# java multithreading mono


【解决方案1】:

运行该程序,我发现它使用了 97%-98% 的 8 个 CPU,但同时也产生了大量的垃圾。如果我让程序更高效,它会更快地运行到完成。

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class JavaMonoTest {

    static final AtomicInteger threadCount = new AtomicInteger();
    static final AtomicInteger completeCount = new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
        final int numberOfCycles = 1000;
        final int numbersPerCycle = 1000000;
        final int NUM_CORES = Runtime.getRuntime().availableProcessors();

        long swG = System.nanoTime();

        ExecutorService exec = Executors.newFixedThreadPool(NUM_CORES);
        try {
            for (int i = 0; i < numberOfCycles; i++) {
                final int x = i;
                exec.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                        System.out.printf("Starting cycle %s. Thread count at %s %n", x, threadCount.getAndIncrement());

                        Random r = new Random();
                        long sw = System.nanoTime();
                        double[] numbers = new double[numbersPerCycle];
                        for (int i = 0; i < numbersPerCycle; i++) {
                            numbers[i] = r.nextDouble() * 1000;
                        }
                        Arrays.sort(numbers);
                        double min = numbers[0];
                        double max = numbers[numbers.length - 1];

                        completeCount.getAndIncrement();
                        System.out.printf("%s cycles complete: %.2f ms. Min: %.2f  Max: %.2f %n",
                                completeCount, (System.nanoTime() - sw) / 1e6, min, max);
                        threadCount.getAndDecrement();
                        } catch (Throwable t) {
                            t.printStackTrace();
                        }
                    }
                });
            }
        } finally {
            exec.shutdown();
        }
        exec.awaitTermination(1, TimeUnit.DAYS);

        System.out.printf("All %s cycles complete. Took %.2f ms. %n",
                numberOfCycles, (System.nanoTime() - swG) / 1e6);
    }
}

打印

Starting cycle 0. Thread count at 0 
Starting cycle 7. Thread count at 7 
Starting cycle 6. Thread count at 6 
   ... deleted ...
999 cycles complete: 139.28 ms. Min: 0.00  Max: 1000.00 
1000 cycles complete: 139.05 ms. Min: 0.00  Max: 1000.00 
All 1000 cycles complete. Took 19431.14 ms. 

【讨论】:

  • 你肯定在一台机器上,我的仍然花了 40 多秒。无论如何都做得很好-我猜Java不像.NET那样优雅地处理垃圾?我希望进行更多的 1:1 比较,因此我将相应地重写 .NET 应用程序。谢谢!
  • 我怀疑您的 .NET 列表等同于 Java 的 ArrayList。 LinkedList 速度很快,但会使用更多内存,如果您没有太多内存,这将是一个问题。它是具有 32 GB 内存的 Dual Xeon 3 GHz,但通常我使用更大的 256 GB 机器,但它忙于工作;)
  • 这可能是问题的一部分——我不是 Java 编码员,我只是知道会惹上麻烦......
  • 我发现最好比较高效的代码以进行基准测试。如果你比较低效的代码,你会得到奇怪的结果,就像你可以编写一个测试表明 Java 比 C 快得多,但那是因为 Java 有一些 C 没有的死代码优化。
  • 更新后的 .NET 版本似乎在我的机器上运行了大约 60 秒,所以现在 Java 获胜了...我将把更新后的代码放在上面。
【解决方案2】:

代替:

   ExecutorService exec = Executors.newFixedThreadPool(NUM_CORES);
   try {
       for (final Integer x : cyclesList) {
           exec.submit(new Runnable() {

尝试:

   ExecutorService exec = Executors.newFixedThreadPool(NUM_CORES);
   try {
       for (final Integer x : cyclesList) {
           exec.execute( new Runnable() {    // No Future< T > needed

【讨论】:

  • 这似乎运行得更好,但我认为仍然有一些东西阻塞了线程。 CPU 使用率会飙升,但仍保持在相对较低的水平。我在numbers.add(r.nextDouble() * 1000); 上也遇到了一些java.lang.OutOfMemoryError: Java heap space 错误——这在以前没有发生过。
  • 通过 -Xmx1G 运行似乎可以修复堆错误,但 CPU 仍然没有像我希望的那样保持活跃。
猜你喜欢
  • 1970-01-01
  • 2017-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-20
  • 1970-01-01
  • 1970-01-01
  • 2016-03-28
相关资源
最近更新 更多