2016-02-26 14 views
6

Sto riscrivendo un'applicazione C++ ad alte prestazioni in C#. L'app C# è notevolmente più lenta dell'originale C++. Il profilo mi dice che l'app C# impiega più tempo ad accedere agli elementi dell'array. Quindi creo un semplice benchmark di accesso alla matrice. Ottengo risultati completamente diversi da others doing a similiar comparison.C++ array vs C# confusione di velocità ptr

Il codice C++:

#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(); 
} 

Il codice C#:

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

Sul mio computer di lavoro (i7-3770), la versione C++ è di circa 2 volte più veloce rispetto alla versione C#. Sul mio computer di casa (i7-5820K) il C++ è 1.5 volte più veloce della versione C#. Entrambi sono misurati in Release. Speravo che usando i puntatori in C# avrei evitato il controllo dei confini dell'array e le prestazioni sarebbero state le stesse in entrambe le lingue.

Quindi le mie domande sono le seguenti:

  • casa prenderà gli altri stanno trovando C# per essere della stessa velocità di C++?
  • Come posso ottenere prestazioni C# al livello C++ se non tramite puntatori?
  • quale potrebbe essere il driver di diverse accelerazioni su diversi computer?

Ogni suggerimento è molto apprezzato, Daniel

+0

Spero che tu stia eseguendo il benchmark senza il debugger (CTRL + F5 invece di F5 in Visual Studio) – xanatos

+0

@xanatos: Sì. Grazie per il collegamento però. –

+0

* ushort * xArrayEnd = xArrayStart + xRepLen; * È possibile spostare questo fuori dal ciclo 'for' – xanatos

risposta

0

Non si ottiene questo tipo di numero di Hardcore sgranocchiare a velocità C++. Usando il codice aritmetico e non sicuro del puntatore si ottiene un po 'di là (è quasi la metà più lento se si rimuovono parti non sicure e fisse). C# non è compilato in codice nativo e il codice che sta eseguendo è pieno di controlli e cose extra.

Se sei disposto ad andare su unsafe, in realtà non c'è nulla che ti impedisca di codificare le tue prestazioni C++ in un assembly in modalità mista e di chiamarlo dal tuo codice di colla C#.

+0

Grazie per avermi indicato in quella direzione. I (piccoli) articoli che ho letto puntano al fatto che il passaggio dal runtime al codice non gestito e viceversa è lento. È davvero così nella tua esperienza? –

+0

Non è come farlo una volta ci vuole molto tempo, ma farlo un sacco di volte in un ciclo sarebbe male. Idealmente si ottiene tutto pronto in C#, poi si passa tutto al C++ e si può fare tutto il ciclo e la matematica. Quindi esegue il backup di questi risultati e puoi tornare a tutti i vantaggi del mondo gestito, dopo aver eseguito i tuoi calcoli in modo rapido e piacevole. –