2016-02-26 8 views
6

Ich überschreibe eine leistungsstarke C++ - Anwendung auf C#. Die C# -App ist merklich langsamer als das C++ - Original. Das Profiling sagt mir, dass die C# -App am meisten Zeit auf den Zugriff auf Array-Elemente verwendet. Daher erstelle ich einen einfachen Array-Access-Benchmark. Ich bekomme völlig andere Ergebnisse als others doing a similiar comparison.C++ - Array vs C# ptr Geschwindigkeit Verwirrung

Der C++ Code:

#include <limits> 
#include <stdio.h> 
#include <chrono> 
#include <iostream> 

using namespace std; 
using namespace std::chrono; 

int main(void) 
{ 
    high_resolution_clock::time_point t1 = high_resolution_clock::now(); 

    int xRepLen = 100 * 1000; 
    int xRepCount = 1000; 

    unsigned short * xArray = new unsigned short[xRepLen]; 
    for (int xIdx = 0; xIdx < xRepLen; xIdx++) 
     xArray[xIdx] = xIdx % USHRT_MAX; 

    int * xResults = new int[xRepLen]; 

    for (int xRepIdx = 0; xRepIdx < xRepCount; xRepIdx++) 
    { 

     // in each repetition, find the first value, that surpasses xArray[xIdx] + 25 - i.e. we will perform 25 searches 
     for (int xIdx = 0; xIdx < xRepLen; xIdx++) 
     { 
      unsigned short xValToBreach = (xArray[xIdx] + 25) % USHRT_MAX; 
      xResults[xIdx] = 0; 

      for (int xIdx2 = xIdx + 1; xIdx2 < xRepLen; xIdx2++) 
      if (xArray[xIdx2] >= xValToBreach) 
      { 
       xResults[xIdx] = xIdx2; break; 
      } 

      if (xResults[xIdx] == 0) 
       xResults[xIdx] = INT_MAX; 
     } 
    } 

    high_resolution_clock::time_point t2 = high_resolution_clock::now(); 
    auto duration = duration_cast<milliseconds>(t2 - t1).count(); 
    cout << "Elasped miliseconds " << duration; 
    getchar(); 
} 

Der C# -Code:

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 

namespace arrayBenchmarkCs 
{ 
    class Program 
    { 
     public static void benchCs() 
     { 
      unsafe 
      { 
       int xRepLen = 100 * 1000; 
       int xRepCount = 1000; 

       ushort[] xArr = new ushort[xRepLen]; 
       for (int xIdx = 0; xIdx < xRepLen; xIdx++) 
        xArr[xIdx] = (ushort)(xIdx % 0xffff); 

       int[] xResults = new int[xRepLen]; 

       Stopwatch xSw = new Stopwatch(); xSw.Start(); 
       fixed (ushort * xArrayStart = & xArr [0]) 
       { 
        for (int xRepIdx = 0; xRepIdx < xRepCount; xRepIdx++) 
        { 

         // in each repetition, go find the first value, that surpasses xArray[xIdx] + 25 - i.e. we will perform 25 searches 
         ushort * xArrayEnd = xArrayStart + xRepLen; 
         for (ushort* xPtr = xArrayStart; xPtr != xArrayEnd; xPtr++) 
         { 
          ushort xValToBreach = (ushort)((*xPtr + 25) % 0xffff); 
          int xResult = -1; 
          for (ushort * xPtr2 = xPtr + 1; xPtr2 != xArrayEnd; xPtr2++) 
           if (*xPtr2 >= xValToBreach) 
           { 
            xResult = (int)(xPtr2 - xArrayStart); 
            break; 
           } 

          if (xResult == -1) 
           xResult = int.MaxValue; 

          // save result 
          xResults[xPtr - xArrayStart] = xResult; 
         } 
        } 
       } // fixed 

       xSw.Stop(); 

       Console.WriteLine("Elapsed miliseconds: " + (xSw.ElapsedMilliseconds.ToString("0")); 
      } 
     } 

     static void Main(string[] args) 
     { 
      benchCs(); 
      Console.ReadKey(); 
     } 
    } 
} 

Auf meinem Computer (i7-3770), die C++ Version ist ca. 2x schneller als die C# -Version. Auf meinem Heimcomputer (i7-5820K) ist das C++ 1.5x schneller als die C# -Version. Beide werden in Release gemessen. Ich hoffte, dass ich durch die Verwendung von Zeigern in C# die Überprüfung der Array-Grenze vermeiden würde und die Leistung in beiden Sprachen gleich wäre.

So sind meine Fragen die folgenden:

  • nach Hause kommen andere zu finden, C# der gleichen Geschwindigkeit wie C++ sein?
  • Wie kann ich C# -Performance auf C++ - Ebene erhalten, wenn nicht über Zeiger?
  • Was könnte der Treiber für verschiedene Beschleunigungen auf verschiedenen Computern sein?

Jeder Hinweis sehr geschätzt wird, Daniel

+0

Ich hoffe, Sie führen den Benchmark ohne den Debugger (STRG + F5 statt F5 in Visual Studio) – xanatos

+0

@xanatos: Ja. Danke für die Abkürzung. –

+0

* ushort * xArrayEnd = xArrayStart + xRepLen; * Sie können dies außerhalb des 'for'-Zyklus verschieben – xanatos

Antwort

0

Sie diese Art von Hardcore Zahlknirschens zu C++ Geschwindigkeit nicht erhalten. Die Verwendung von Zeigerarithmetik und unsicherem Code bringt Sie dorthin (es ist fast wieder halb so langsam, wenn Sie die unsicheren und festen Teile entfernen). C# wird nicht in systemeigenen Code kompiliert, und der Code, der ausgeführt wird, ist voll von zusätzlichen Prüfungen und anderem.

Wenn Sie bereit sind, unsafe zu gehen, dann gibt es wirklich nichts, was Sie daran hindert, Ihre C++ - Performance-kritischen Sachen in eine Assembly im gemischten Modus zu schreiben und diese aus Ihrem C# -Leimcode heraus aufzurufen.

+0

Danke, dass du mich in diese Richtung gelenkt hast. Die (kleine Menge von) Artikeln, die ich gelesen habe, weisen darauf hin, dass die Laufzeit von verwaltetem zu nicht verwaltetem Code und umgekehrt langsam ist. Ist das wirklich so in deiner Erfahrung? –

+0

Es ist nicht so, als würde es lange dauern, aber es wäre schlecht, es oft in einer Schleife zu machen. Im Idealfall erhalten Sie alles in C# bereit, dann geben Sie alles an C++ ab und können Schleifen und Mathe ausführen. Dann packt es diese Ergebnisse wieder und Sie können zu allen Vorteilen der verwalteten Welt zurückkehren, nachdem Sie Ihre Berechnungen gut und schnell durchgeführt haben. –