12

私は最近、スレッドプールでスケジュールされる1000のタスクについて、JavaとC#でベンチマークを実行しています。サーバーには4つの物理プロセッサがあり、それぞれに8つのコアがあります。OSはServer2008で、32 GBのメモリがあり、各CPUはXeon x7550 Westmere/Nehalem-Cです。

つまり、Javaの実装は4スレッドでC#よりもはるかに高速ですが、スレッド数が増えるとはるかに遅くなります。また、スレッド数が増えると、C#は反復ごとに速くなったようです。グラフはこの投稿に含まれています:

スレッドプールサイズが4スレッドのJavaとC# スレッドプールサイズが32スレッドのJavaとC# PeterのJava回答(以下を参照)とC#、32スレッド

Java実装は、64ビットのホットスポットJVMで、Java 7を使用し、オンラインで見つけたエグゼキューターサービススレッドプールを使用して作成されました(以下を参照)。また、JVMを並行GCに設定しました。

C#は.net 3.5で記述され、スレッドプールはcodeprojectから取得されました:http: //www.codeproject.com/Articles/7933/Smart-Thread-Pool

(私は以下のコードを含めました)。

私の質問:

1)Javaが遅くなっているのに、C#が速くなっているのはなぜですか?

2)C#の実行時間が大きく変動するのはなぜですか?(これが私たちの主な質問です)

C#の変動は、メモリバスが最大になっていることが原因であるのかどうか疑問に思いました。

コード(ロックのエラーを強調しないでください。これは私の目的とは無関係です):

Java

import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class PoolDemo {

    static long FastestMemory = 2000000000;
    static long SlowestMemory = 0;
    static long TotalTime;
    static long[] FileArray;
    static DataOutputStream outs;
    static FileOutputStream fout;

  public static void main(String[] args) throws InterruptedException, FileNotFoundException {

        int Iterations = Integer.parseInt(args[0]);
        int ThreadSize = Integer.parseInt(args[1]);

        FileArray = new long[Iterations];
        fout = new FileOutputStream("server_testing.csv");

        // fixed pool, unlimited queue
        ExecutorService service = Executors.newFixedThreadPool(ThreadSize);
        //ThreadPoolExecutor executor = (ThreadPoolExecutor) service;

        for(int i = 0; i<Iterations; i++) {
          Task t = new Task(i);
          service.execute(t);
        }

        service.shutdown();
        service.awaitTermination(90, TimeUnit.SECONDS);

        System.out.println("Fastest: " + FastestMemory);
        System.out.println("Average: " + TotalTime/Iterations);

        for(int j=0; j<FileArray.length; j++){
            new PrintStream(fout).println(FileArray[j] + ",");
        }
      }

  private static class Task implements Runnable {

        private int ID;

        static Byte myByte = 0;

        public Task(int index) {
          this.ID = index;
        }

        @Override
        public void run() {
            long Start = System.nanoTime();

          int Size1 = 10000000;
            int Size2 = 2 * Size1;
            int Size3 = Size1;

            byte[] list1 = new byte[Size1];
            byte[] list2 = new byte[Size2];
            byte[] list3 = new byte[Size3];

            for(int i=0; i<Size1; i++){
                list1[i] = myByte;
            }

            for (int i = 0; i < Size2; i=i+2)
            {
                list2[i] = myByte;
            }

            for (int i = 0; i < Size3; i++)
            {
                byte temp = list1[i];
                byte temp2 = list2[i];
                list3[i] = temp;
                list2[i] = temp;
                list1[i] = temp2;
            }

            long Finish = System.nanoTime();
            long Duration = Finish - Start;
            FileArray[this.ID] = Duration;
            TotalTime += Duration;
            System.out.println("Individual Time " + this.ID + " \t: " + (Duration) + " nanoseconds");


            if(Duration < FastestMemory){
                FastestMemory = Duration;
            }
            if (Duration > SlowestMemory)
            {
                SlowestMemory = Duration;
            }
        }
      }
}

C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Amib.Threading;
using System.Diagnostics;
using System.IO;
using System.Runtime;


namespace ServerTesting
{
    class Program
    {
        static long FastestMemory = 2000000000;
        static long SlowestMemory = 0;
        static long TotalTime = 0;
        static int[] FileOutput;
        static byte myByte = 56;

        static System.IO.StreamWriter timeFile;
        static System.IO.StreamWriter memoryFile;

        static void Main(string[] args)
        {
            Console.WriteLine("Concurrent GC enabled: " + GCSettings.IsServerGC);
            int Threads =   Int32.Parse(args[1]);
            int Iterations = Int32.Parse(args[0]);

            timeFile = new System.IO.StreamWriter(Threads + "_" + Iterations + "_" + "time.csv");

            FileOutput = new int[Iterations];
            TestMemory(Threads, Iterations);

            for (int j = 0; j < Iterations; j++)
            {
                timeFile.WriteLine(FileOutput[j] + ",");
            }

            timeFile.Close();
            Console.ReadLine();
        }

        private static void TestMemory(int threads, int iterations)
        {
            SmartThreadPool pool = new SmartThreadPool();
            pool.MaxThreads = threads;
            Console.WriteLine("Launching " + iterations + " calculators with " + pool.MaxThreads + " threads");
            for (int i = 0; i < iterations; i++)
            {
                pool.QueueWorkItem(new WorkItemCallback(MemoryIntensiveTask), i);
            }
            pool.WaitForIdle();
            double avg = TotalTime/iterations;
            Console.WriteLine("Avg Memory Time : " + avg);
            Console.WriteLine("Fastest: " + FastestMemory + " ms");
            Console.WriteLine("Slowest: " + SlowestMemory + " ms");
        }



        private static object MemoryIntensiveTask(object args)
        {

            DateTime start = DateTime.Now;
            int Size1 = 10000000;
            int Size2 = 2 * Size1;
            int Size3 = Size1;

            byte[] list1 = new byte[Size1];
            byte[] list2 = new byte[Size2];
            byte[] list3 = new byte[Size3];

            for (int i = 0; i < Size1; i++)
            {
                list1[i] = myByte;
            }

            for (int i = 0; i < Size2; i = i + 2)
            {
                list2[i] = myByte;
            }

            for (int i = 0; i < Size3; i++)
            {
                byte temp = list1[i];
                byte temp2 = list2[i];
                list3[i] = temp;
                list2[i] = temp;
                list1[i] = temp2;
            }

            DateTime finish = DateTime.Now;
            TimeSpan ts = finish - start;
            long duration = ts.Milliseconds;

            Console.WriteLine("Individual Time " + args + " \t: " + duration);

            FileOutput[(int)args] = (int)duration;
            TotalTime += duration;

            if (duration < FastestMemory)
            {
                FastestMemory = duration;
            }
            if (duration > SlowestMemory)
            {
                SlowestMemory = duration;
            }
            return null;
        }
    }
}
4

1 に答える 1

12

言語が最適化されていないコードを最適化する方法をテストしているほど、スレッドフレームの動作をテストしているようには見えません。

Javaは無意味なコードの最適化に特に優れており、言語の違いを説明できると思います。スレッドの数が増えるにつれて、ボトルネックがGCのパフォーマンスや、テストに付随する何かに移行するのではないかと思います。

Javaは、デフォルトではNUMAに対応していないため、速度が低下する可能性もあります。実行してみてください-XX:+UseNUMA ただし、最大のパフォーマンスを得るには、クロスヌマのオーバーヘッドを回避するために、各プロセスを単一のヌマ領域に保つようにすることをお勧めします。

私のマシンで40%高速だったこのわずかに最適化されたコードを試すこともできます

import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class PoolDemo {

    static long FastestMemory = 2000000000;
    static long SlowestMemory = 0;
    static long TotalTime;
    static long[] FileArray;
    static FileOutputStream fout;

    public static void main(String[] args) throws InterruptedException, FileNotFoundException {

        int Iterations = Integer.parseInt(args[0]);
        int ThreadSize = Integer.parseInt(args[1]);

        FileArray = new long[Iterations];
        fout = new FileOutputStream("server_testing.csv");

        // fixed pool, unlimited queue
        ExecutorService service = Executors.newFixedThreadPool(ThreadSize);
        //ThreadPoolExecutor executor = (ThreadPoolExecutor) service;

        for (int i = 0; i < Iterations; i++) {
            Task t = new Task(i);
            service.execute(t);
        }

        service.shutdown();
        service.awaitTermination(90, TimeUnit.SECONDS);

        System.out.println("Fastest: " + FastestMemory);
        System.out.println("Average: " + TotalTime / Iterations);

        PrintStream ps = new PrintStream(fout);
        for (long aFileArray : FileArray) {
            ps.println(aFileArray + ",");
        }
    }

    static class ThreadLocalBytes extends ThreadLocal<byte[]> {
        private final int bytes;

        ThreadLocalBytes(int bytes) {
            this.bytes = bytes;
        }

        @Override
        protected byte[] initialValue() {
            return new byte[bytes];
        }
    }

    private static class Task implements Runnable {

        static final int Size1 = 10000000;
        static final int Size2 = 2 * Size1;
        static final int Size3 = Size1;

        private int ID;
        private static final ThreadLocalBytes list1b = new ThreadLocalBytes(Size1);
        private static final ThreadLocalBytes list2b = new ThreadLocalBytes(Size2);
        private static final ThreadLocalBytes list3b = new ThreadLocalBytes(Size3);

        static byte myByte = 0;

        public Task(int index) {
            this.ID = index;
        }

        @Override
        public void run() {
            long Start = System.nanoTime();


            byte[] list1 = list1b.get();
            byte[] list2 = list2b.get();
            byte[] list3 = list3b.get();

            for (int i = 0; i < Size1; i++) {
                list1[i] = myByte;
            }

            for (int i = 0; i < Size2; i = i + 2) {
                list2[i] = myByte;
            }

            for (int i = 0; i < Size3; i++) {
                byte temp = list1[i];
                byte temp2 = list2[i];
                list3[i] = temp;
                list2[i] = temp;
                list1[i] = temp2;
            }

            long Finish = System.nanoTime();
            long Duration = Finish - Start;
            FileArray[this.ID] = Duration;
            TotalTime += Duration;
            System.out.println("Individual Time " + this.ID + " \t: " + (Duration) + " nanoseconds");

            if (Duration < FastestMemory) {
                FastestMemory = Duration;
            }
            if (Duration > SlowestMemory) {
                SlowestMemory = Duration;
            }
        }
    }
}
于 2012-04-05T11:55:06.427 に答える