2012-04-05 12 views
9

Gần đây tôi đã chạy điểm chuẩn trên Java vs C# cho 1000 tác vụ được lên lịch trên một luồng. Máy chủ có 4 bộ xử lý vật lý, mỗi bộ xử lý có 8 lõi. Hệ điều hành là Server 2008, có 32 GB bộ nhớ và mỗi CPU là một Xeon x7550 Westmere/Nehalem-C.Java vs C# Hiệu suất đa luồng, tại sao Java lại chậm hơn? (đồ thị và mã đầy đủ bao gồm)

Tóm lại, việc triển khai Java nhanh hơn nhiều so với C# ở 4 luồng nhưng chậm hơn nhiều khi số lượng chủ đề tăng lên. Dường như C# đã trở nên nhanh hơn cho mỗi lần lặp lại, khi số lượng chỉ số đã tăng lên. Đồ thị có trong bài này:

Java vs C# with a threadpool size of 4 threads Java vs C# with a threadpool size of 32 threads Peter's Java answer (see below) vs C#, for 32 threads

Việc thực hiện Java được viết trên một 64bit Hotspot JVM, với Java 7 và sử dụng một dịch vụ Executor threadpool Tôi tìm thấy trực tuyến (xem dưới đây). Tôi cũng đặt JVM thành GC đồng thời.

C# được viết trên .net 3.5 và threadpool đến từ CodeProject: http://www.codeproject.com/Articles/7933/Smart-Thread-Pool

(Tôi đã bao gồm mã dưới đây).

Câu hỏi của tôi:

1) Tại sao Java bị chậm hơn nhưng C# đang nhanh hơn?

2) Tại sao thời gian thực thi của C# dao động đáng kể? (Đây là câu hỏi chính của chúng tôi)

Chúng tôi đã tự hỏi liệu C# biến động là do xe buýt nhớ được maxed ra ....

Mã (Xin đừng làm nổi bật lỗi với khóa, điều này là không thích hợp với tôi nhằm mục đích):

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; 
     } 
    } 
} 
+7

đã bạn nhận thấy rằng trong Java vòng lặp của bạn tạo ra một 'PrintStream' trong mỗi lần lặp trong khi trong 'C#' bạn mở 'StreamWriter' chỉ một lần? Tôi không nghĩ rằng nó giải thích hiện tượng này, nhưng thử nghiệm của bạn không chính xác 100% ở mức cơ bản. – RonK

+0

Bạn đã thử lớp ThreadPoolExecutor chưa? ThreadPoolExecutor được cho là cung cấp hiệu suất được cải thiện cho số lượng lớn các tác vụ không đồng bộ. – ChadNC

+0

@ ChanNC- cảm ơn tôi sẽ thử điều đó. – mezamorphic

Trả lời

5

Y Có vẻ như bạn không thử nghiệm khung luồng hoạt động nhiều như bạn đang thử nghiệm cách ngôn ngữ tối ưu hóa mã chưa được tối ưu hóa.

Java đặc biệt giỏi tối ưu hóa mã không điểm, mà tôi tin rằng sẽ giải thích sự khác biệt về ngôn ngữ. Khi số lượng các chủ đề phát triển, tôi nghi ngờ cổ chai di chuyển đến cách GC thực hiện hoặc một số điều khác ngẫu nhiên để thử nghiệm của bạn.

Java cũng có thể bị chậm lại do không nhận biết được NUMA theo mặc định. Hãy thử chạy -XX:+UseNUMA Tuy nhiên tôi đề nghị cho hiệu suất tối đa, bạn nên cố gắng giữ cho mỗi quá trình để một khu vực numa duy nhất để tránh qua numa trên không.

Bạn cũng có thể thử loại này hơi tối ưu hóa mã mà là 40% nhanh trên máy tính của tôi

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; 
      } 
     } 
    } 
} 
+1

Peter Tôi vừa thử các công cụ hàng đầu của bạn! Bạn có thể giải thích lý do tại sao các thay đổi của bạn tạo ra sự khác biệt như vậy không? Xin lỗi để gánh nặng bạn .... Ngoài ra bạn có biết tại sao mã C# dao động quá nhiều? Đây là một trong những lo lắng lớn nhất của chúng tôi khi toàn bộ thư viện của chúng tôi được viết bằng C#. – mezamorphic

+2

Khi bạn sử dụng nhiều chủ đề hơn, các tài nguyên được chia luồng/chia sẻ đơn giản trở nên quan trọng hơn. Trong trường hợp này GC là tài nguyên được chia sẻ. Trong ví dụ trên, tôi tái chế các bộ đệm của mình, không chỉ làm cho nó nhanh hơn mà còn làm giảm jitter (tạm dừng để làm sạch). Tôi chưa bao giờ chạm vào C# (với bất kỳ mức độ nào mà tôi sẽ thừa nhận) nhưng tôi giả sử cùng một cách làm việc trong Java, để cpu và bộ nhớ hồ sơ ứng dụng của bạn. Hãy chắc chắn rằng bạn có một công cụ bạn có thể tin tưởng. VisualVM không phải là một ví dụ tuyệt vời (nhưng nó là miễn phí;) –

+0

Hey peter, tôi đồ thị tốc độ 40% và cho 25 lần lặp đầu tiên, java mất khoảng 2450 ms, nhưng sau đó cho phần còn lại của 975 lặp mất khoảng 450ms- điều này là do GC và có bất kỳ sự tối ưu nào tôi có thể sử dụng để tránh tắc nghẽn ban đầu này không? Cảm ơn bạn- câu trả lời của bạn được đánh giá cao. – mezamorphic

Các vấn đề liên quan