2012-04-05 21 views
9

मैं हाल ही में एक थ्रेडपूल पर निर्धारित 1000 कार्यों के लिए जावा बनाम सी # पर बेंचमार्क चला रहा हूं। सर्वर में 4 भौतिक प्रोसेसर हैं, प्रत्येक 8 कोर के साथ। ओएस सर्वर 2008 है, 32 जीबी मेमोरी है और प्रत्येक सीपीयू एक ज़ीऑन x7550 वेस्टमेरे/नेहलेम-सी है।जावा बनाम सी # मल्टीथ्रेडिंग प्रदर्शन, जावा धीमा क्यों हो रहा है? (ग्राफ और पूर्ण कोड शामिल)

संक्षेप में, जावा कार्यान्वयन 4 धागे पर सी # से बहुत तेज है लेकिन धागे की संख्या बढ़ने के साथ बहुत धीमी है। ऐसा लगता है कि सी # प्रति पुनरावृत्ति तेज हो गया है, जब थ्रेड गिनती बढ़ गई है। रेखांकन इस पोस्ट में शामिल हैं:

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

जावा कार्यान्वयन जावा 7 के साथ, एक 64 बिट हॉटस्पॉट JVM पर लिखा गया है और एक निर्वाहक सेवा का उपयोग ThreadPool मैं ऑनलाइन पाया (नीचे देखें) किया गया था। मैंने समवर्ती जीसी के लिए जेवीएम भी सेट किया।

सी # .net 3.5 पर लिखा गया था और ThreadPool codeproject से आया था: http://www.codeproject.com/Articles/7933/Smart-Thread-Pool

(मैं नीचे दिए गए कोड को शामिल किया है)।

मेरे सवालों का:

1) क्यों जावा धीमी हो रही है, लेकिन सी # तेज हो रही है?

2) सी # के निष्पादन के समय क्यों उतार-चढ़ाव करते हैं? (यह हमारे मुख्य सवाल यह है कि)

हम आश्चर्य कहा कि क्या सी # में उतार-चढ़ाव स्मृति बस के कारण हुई थी बाहर maxed किया जा रहा ....

कोड (कृपया लॉकिंग के साथ त्रुटियों को उजागर नहीं करते हैं, यह अप्रासंगिक साथ है मेरी करना है):

जावा

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

सी #:

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

आपको लगता है कि जावा में अपने पाश 'सी # में जबकि प्रत्येक चरण में एक' PrintStream' बनाता नोटिस 'आप खोलना केवल एक बार स्ट्रीमवाइटर? मुझे नहीं लगता कि यह घटना को समझाता है, लेकिन आपका परीक्षण मूल स्तर पर 100% सटीक नहीं है। – RonK

+0

क्या आपने ThreadPoolExecutor क्लास की कोशिश की है? ThreadPoolExecutor को बड़ी संख्या में असीमित कार्यों के लिए बेहतर प्रदर्शन प्रदान करना माना जाता है। – ChadNC

+0

@ चैनएनसी- धन्यवाद मैं इसे आजमाउंगा। – mezamorphic

उत्तर

5

Y कहां थ्रेडिंग फ्रेम काम का परीक्षण नहीं कर रहे हैं जितना आप परीक्षण कर रहे हैं कि भाषा गैर-अनुकूलित कोड को कैसे अनुकूलित करती है।

जावा व्यर्थ कोड को अनुकूलित करने में विशेष रूप से अच्छा है, जो मुझे विश्वास है कि भाषाओं में अंतर बताएगा। चूंकि धागे की संख्या बढ़ती है, मुझे संदेह है कि बोतल गर्दन जीसी कैसे करती है या आपके परीक्षण के लिए कुछ और आकस्मिक है।

जावा भी धीमा हो सकता है क्योंकि यह डिफ़ॉल्ट रूप से 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; 
      } 
     } 
    } 
} 
+1

पीटर मैंने अभी आपके कोड-टॉप सामान की कोशिश की है! क्या आप विस्तार से बता सकते हैं कि आपके परिवर्तनों ने इतना अंतर क्यों बनाया? आपको बोझ करने के लिए खेद है .... क्या आपको पता है कि सी # कोड इतना उतार-चढ़ाव क्यों करता है? यह हमारी सबसे बड़ी चिंताओं में से एक है क्योंकि हमारी पूरी लाइब्रेरी सी # में लिखी गई है। – mezamorphic

+2

जब आप अधिक धागे का उपयोग करते हैं, तो एकल थ्रेडेड/साझा संसाधन अधिक से अधिक महत्वपूर्ण हो जाते हैं। इस मामले में जीसी एक साझा संसाधन है। ऊपर दिए गए उदाहरण में मैं अपने बफर को रीसायकल करता हूं जो न केवल इसे तेज बनाता है बल्कि जिटर को कम करता है (साफ अप करने के लिए रोकता है) मैंने कभी सी # को छुआ नहीं है (किसी भी डिग्री के लिए मैं स्वीकार करूंगा) लेकिन मुझे लगता है कि जावा में काम करने वाला एक ही दृष्टिकोण है, है सीपीयू और मेमोरी प्रोफाइल आपके आवेदन के लिए। सुनिश्चित करें कि आपके पास एक उपकरण है जिस पर आप भरोसा कर सकते हैं। विजुअलVM एक महान उदाहरण नहीं है (लेकिन यह मुफ़्त है;) –

+0

हे पीटर, मैंने 40% स्पीडअप को पकड़ लिया और पहले 25 पुनरावृत्तियों के लिए जावा में लगभग 2450 एमएस लगते हैं, लेकिन शेष 975 पुनरावृत्तियों के लिए इसमें लगभग 450 एमएस- क्या यह जीसी के कारण है और क्या कोई ऐसी ऑप्टिमाइज़ेशन है जिसका उपयोग मैं इस प्रारंभिक बाधा से बचने के लिए कर सकता हूं? धन्यवाद - आपके उत्तरों की बहुत सराहना की जाती है। – mezamorphic