Domanda su un "Benchmark" riguardante Java e .NET

4

Per farlo, non sto chiedendo quale piattaforma sia più veloce o cerchi di iniziare una guerra di fuoco.

Domanda: Perché la versione .NET è più lenta di 5x nel primo test? I risultati che ho ottenuto sono stati:

.NET Test1: 48992696 Test2: 1210070

Java Test1: 9651995 Test2: 1029374

Questo è strettamente dietro le implementazioni delle liste? Se sì, c'è qualcosa per rendere più rapido l'inserimento delle liste sul lato .NET?

EDIT:

Utilizzati elenchi preallocati e rimossi int interi, portati a

.NET Test1: 11 838 363

Java Test1: 712 481

.NET

namespace TestApp
{
    class Program
    {
        static void Main(string[] args) {
            long samples = 100;
            long test1Average = 0;
            for (int i = 0; i < samples; i++) {
                test1Average += runTest1(); 
            }
            test1Average = test1Average / samples;
            System.Diagnostics.Debug.WriteLine("TEST 1 AVERAGE: " + test1Average);

            List<String> list = new List<String>();
            for (int i = 0; i < 100000; i++)
            {
                list.Add("String: " + i);
            }

            long test2Average = 0;
            for (int i = 0; i < samples; i++)
            {
                test2Average += runTest2(list);
            }
            test2Average = test2Average / samples;
            System.Diagnostics.Debug.WriteLine("TEST 2 AVERAGE: " + test2Average);
        }

        private static long runTest1()
        {
            List<String> list = new List<String>();
            long time = System.DateTime.Now.Ticks;
            for (int i = 0; i < 100000; i++)
            {
                list.Add("String: " + i);
            }
            return (System.DateTime.Now.Ticks - time) * 100;
        }

        private static long runTest2(List<String> list)
        {
            long time = System.DateTime.Now.Ticks;
            foreach (String s in list) {
                s.ToString();
            }
            return (System.DateTime.Now.Ticks - time) * 100;
        }

    }
}

Java

public class Main {

public static void main(String[] args) {
    long samples =  100;
    long test1Average = 0;
    for (int i = 0; i < samples; i++) {
        test1Average += runTest1();
    }
    test1Average = test1Average / samples;
    System.out.println("TEST 1 AVERAGE: "+test1Average);

    List<String> list = new ArrayList<String>();
    for (int i = 0; i < 100000; i++) {
        list.add("String: " + i);
    }
    long test2Average = 0;
    for (int i = 0; i < samples; i++) {
        test2Average += runTest2(list);
    }
    test2Average = test2Average / samples;
    System.out.println("TEST 2 AVERAGE: "+test2Average);
}

private static long runTest1() {
    long time = System.nanoTime();
    List<String> list = new ArrayList<String>();
    for (int i = 0; i < 100000; i++) {
        list.add("String: " + i);
    }
    return System.nanoTime()-time;
}

private static long runTest2(List<String> list) {
    long time = System.nanoTime();
    for (String s : list) {
        s.toString();
    }
    return System.nanoTime()-time;
}

}

    
posta Dante 31.12.2011 - 18:35
fonte

3 risposte

2

Questa è una domanda eccellente. Ho eseguito il test C # sul mio computer e ho ottenuto risultati molto simili:

TEST 1 MEDIA: 47,812,500

TEST 2 MEDIA: 1.562.500

Questo è ciò che ottengo sostituendo list.Add("String: " + i); in runTest1() ...

... con String s = "String: " + i; :

TEST 1 MEDIA: 19.843.750

TEST 2 MEDIA: 1.406.250

... con list.Add( "moo" ); :

TEST 1 MEDIA: 2.343.750

TEST 2 MEDIA: 1.406.250

Modifica

... con list.Add( i.ToString() );

TEST 1 MEDIA: 20.312.500

TEST 2 MEDIA: 1.093.750 (< - BTW, questo dimostra che questi benchmark non sono molto accurati)

... con String s = i.ToString();

TEST 1 MEDIA: 14.531.250

TEST 2 MEDIA: 1.406.250

... con String s = i.ToString( System.Globalization.CultureInfo.InvariantCulture );

TEST 1 MEDIA: 13.906.250

TEST 2 MEDIA: 1.406.250

    
risposta data 31.12.2011 - 20:37
fonte
2

In breve non posso darti la risposta perché di certo non ne so abbastanza su .NET, ma come puoi vedere di solito non è solo una semplice questione di confronto tra codice Java vs C # di alto livello ...

Dichiarazione di non responsabilità: c'è un sacco di cose che so che mi mancano nel mio rammentare di seguito. Non sono un esperto di prestazioni / benchmark, ma alcuni dei miei colleghi ne interpretano uno nella vita reale: -).

È molto difficile confrontare le mele con le mele tra le due piattaforme. Volevi davvero farlo nel modo più duro e scientifico, dovresti fare cose del tipo:

  • Guarda le implementazioni sottostanti delle strutture dati coinvolte
  • Guarda quale bytecode hanno generato entrambi i gruppi di codice
  • Guarda in che modo le rispettive VM sono state allineate e hanno eseguito quel bytecode

Immagino che tu abbia eseguito entrambi i test sullo stesso hardware e preso in considerazione aspetti come:

  • Permetti alle macchine virtuali di "scaldarsi".
  • Avere impostazioni VM simili come GC, allocazione di memoria e altri tuning

Oh e quali versioni di Java e .NET e quali parametri stai usando le VM sotto?

    
risposta data 31.12.2011 - 18:58
fonte
1

Is this strictly behind list implementations?

Ci sono diverse cose che influenzano le prestazioni di quei test.

  • Uno è il fatto che stai usando DateTime . Non è destinato a misurare le prestazioni, e suppongo che sia molto lento quando si tratta di profilazione. Dal momento che lo stai usando all'interno del loop, rende l'intero ciclo più lento.

  • Il secondo è che stai aggiungendo elementi attraverso un loop a una lista vuota. Quando si aggiunge un elemento e non c'è abbastanza spazio, l'elenco deve espandersi di dimensioni. Costa risorse.

If yes, is there something to make list insertion faster on .NET side?

Ecco lo stesso codice con alcuni refactoring. Il cambiamento più importante è che invece di creare una lista vuota, quindi aggiungendo gli elementi uno per uno, l'elenco viene creato direttamente con tutti gli elementi all'interno.

Purtroppo, vedo solo il 10-15% di miglioramento delle prestazioni, che è ancora ben oltre lo stesso codice in esecuzione in Java. Continuo a cercare perché c'è un'enorme differenza, ma non ho ancora una soluzione.

namespace Delme__benchmark_
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;

    public class Program
    {
        public static void Main()
        {
            Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));

            int countSamples = 100;

            var samples = Enumerable.Range(0, countSamples).ToList();
            var list = Enumerable
                .Range(0, 100000)
                .Select(c => string.Concat("String: ", c)).ToList();

            Trace.WriteLine("Running the benchmark...");
            Trace.WriteLine(string.Format(
                "Test 1 average: {0}.",
                samples.Average(c => RunTest1())));
            Trace.WriteLine(string.Format(
                "Test 2 average: {0}.",
                samples.Average(c => RunTest2(list))));
        }

        private static long RunTest1()
        {
            Stopwatch time = Stopwatch.StartNew();

            var list = Enumerable
                .Range(0, 100000)
                .Select(c => string.Concat("String: ", c)).ToList();

            time.Stop();
            return time.ElapsedTicks;
        }

        private static long RunTest2(IEnumerable<string> list)
        {
            Stopwatch time = Stopwatch.StartNew();

            foreach (var s in list)
            {
                s.ToString();
            }

            time.Stop();
            return time.ElapsedTicks;
        }
    }
}
    
risposta data 31.12.2011 - 20:03
fonte

Leggi altre domande sui tag