web-dev-qa-db-fra.com

Analyse comparative des performances des contenus, des éléments existants et de tout type

Je recherchais une analyse comparative des performances entre les méthodes Contains, Exists et Any disponibles dans le List<T>. Je voulais le découvrir par curiosité, car je me sentais toujours confus parmi eux. Beaucoup de questions sur SO ont décrit les définitions de ces méthodes, telles que:

  1. Anneau LINQ: N'importe lequel () vs contient () pour les collections énormes
  2. Linq .Any VS .Exists - Quelle est la différence?
  3. Méthodes d'extension LINQ - Any () vs. Where () vs. Exists ()

J'ai donc décidé de le faire moi-même. Je l'ajoute comme réponse. Toute autre idée sur les résultats est la bienvenue. J'ai également fait cette analyse comparative pour les tableaux pour voir les résultats

57
harshit

Selon la documentation:

List.Exists (méthode Object)

Détermine si la liste (T) contient des éléments correspondant aux conditions définies par le prédicat spécifié.

IEnumerable.Any (méthode d'extension)

Détermine si un élément d'une séquence vérifie une condition.

List.Contains (Méthode Object)

Détermine si un élément est dans la liste.

Analyse comparative:

CODE:

    static void Main(string[] args)
    {
        ContainsExistsAnyShort();

        ContainsExistsAny();
    }

    private static void ContainsExistsAny()
    {
        Console.WriteLine("***************************************");
        Console.WriteLine("********* ContainsExistsAny ***********");
        Console.WriteLine("***************************************");

        List<int> list = new List<int>(6000000);
        Random random = new Random();
        for (int i = 0; i < 6000000; i++)
        {
            list.Add(random.Next(6000000));
        }
        int[] arr = list.ToArray();

        find(list, arr);
    }

    private static void ContainsExistsAnyShort()
    {
        Console.WriteLine("***************************************");
        Console.WriteLine("***** ContainsExistsAnyShortRange *****");
        Console.WriteLine("***************************************");

        List<int> list = new List<int>(2000);
        Random random = new Random();
        for (int i = 0; i < 2000; i++)
        {
            list.Add(random.Next(6000000));
        }
        int[] arr = list.ToArray();

        find(list, arr);
    }

    private static void find(List<int> list, int[] arr)
    {
        Random random = new Random();
        int[] find = new int[10000];
        for (int i = 0; i < 10000; i++)
        {
            find[i] = random.Next(6000000);
        }

        Stopwatch watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 10000; rpt++)
        {
            list.Contains(find[rpt]);
        }
        watch.Stop();
        Console.WriteLine("List/Contains: {0:N0}ms", watch.ElapsedMilliseconds);

        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 10000; rpt++)
        {
            list.Exists(a => a == find[rpt]);
        }
        watch.Stop();
        Console.WriteLine("List/Exists: {0:N0}ms", watch.ElapsedMilliseconds);

        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 10000; rpt++)
        {
            list.Any(a => a == find[rpt]);
        }
        watch.Stop();
        Console.WriteLine("List/Any: {0:N0}ms", watch.ElapsedMilliseconds);

        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 10000; rpt++)
        {
            arr.Contains(find[rpt]);
        }
        watch.Stop();
        Console.WriteLine("Array/Contains: {0:N0}ms", watch.ElapsedMilliseconds);

        Console.WriteLine("Arrays do not have Exists");

        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 10000; rpt++)
        {
            arr.Any(a => a == find[rpt]);
        }
        watch.Stop();
        Console.WriteLine("Array/Any: {0:N0}ms", watch.ElapsedMilliseconds);
    }

RÉSULTATS

***************************************
***** ContainsExistsAnyShortRange *****
***************************************
List/Contains: 96ms
List/Exists: 146ms
List/Any: 381ms
Array/Contains: 34ms
Arrays do not have Exists
Array/Any: 410ms
***************************************
********* ContainsExistsAny ***********
***************************************
List/Contains: 257,996ms
List/Exists: 379,951ms
List/Any: 884,853ms
Array/Contains: 72,486ms
Arrays do not have Exists
Array/Any: 1,013,303ms
65
harshit

Le moyen le plus rapide consiste à utiliser un HashSet. Le Contains pour un HashSet est O (1).

Je vous ai pris le code et ajouté un point de référence pour HashSet<int> Le coût de performance de HashSet<int> set = new HashSet<int>(list); est presque nul.

void Main()
{
    ContainsExistsAnyShort();

    ContainsExistsAny();
}

private static void ContainsExistsAny()
{
    Console.WriteLine("***************************************");
    Console.WriteLine("********* ContainsExistsAny ***********");
    Console.WriteLine("***************************************");

    List<int> list = new List<int>(6000000);
    Random random = new Random();
    for (int i = 0; i < 6000000; i++)
    {
        list.Add(random.Next(6000000));
    }
    int[] arr = list.ToArray();
    HashSet<int> set = new HashSet<int>(list);

    find(list, arr, set);

}

private static void ContainsExistsAnyShort()
{
    Console.WriteLine("***************************************");
    Console.WriteLine("***** ContainsExistsAnyShortRange *****");
    Console.WriteLine("***************************************");

    List<int> list = new List<int>(2000);
    Random random = new Random();
    for (int i = 0; i < 2000; i++)
    {
        list.Add(random.Next(6000000));
    }
    int[] arr = list.ToArray();
    HashSet<int> set = new HashSet<int>(list);

    find(list, arr, set);

}

private static void find(List<int> list, int[] arr, HashSet<int> set)
{
    Random random = new Random();
    int[] find = new int[10000];
    for (int i = 0; i < 10000; i++)
    {
        find[i] = random.Next(6000000);
    }

    Stopwatch watch = Stopwatch.StartNew();
    for (int rpt = 0; rpt < 10000; rpt++)
    {
        list.Contains(find[rpt]);
    }
    watch.Stop();
    Console.WriteLine("List/Contains: {0}ms", watch.ElapsedMilliseconds);

    watch = Stopwatch.StartNew();
    for (int rpt = 0; rpt < 10000; rpt++)
    {
        list.Exists(a => a == find[rpt]);
    }
    watch.Stop();
    Console.WriteLine("List/Exists: {0}ms", watch.ElapsedMilliseconds);

    watch = Stopwatch.StartNew();
    for (int rpt = 0; rpt < 10000; rpt++)
    {
        list.Any(a => a == find[rpt]);
    }
    watch.Stop();
    Console.WriteLine("List/Any: {0}ms", watch.ElapsedMilliseconds);

    watch = Stopwatch.StartNew();
    for (int rpt = 0; rpt < 10000; rpt++)
    {
        arr.Contains(find[rpt]);
    }
    watch.Stop();
    Console.WriteLine("Array/Contains: {0}ms", watch.ElapsedMilliseconds);

    Console.WriteLine("Arrays do not have Exists");

    watch = Stopwatch.StartNew();
    for (int rpt = 0; rpt < 10000; rpt++)
    {
        arr.Any(a => a == find[rpt]);
    }
    watch.Stop();
    Console.WriteLine("Array/Any: {0}ms", watch.ElapsedMilliseconds);

    watch = Stopwatch.StartNew();
    for (int rpt = 0; rpt < 10000; rpt++)
    {
        set.Contains(find[rpt]);
    }
    watch.Stop();
    Console.WriteLine("HashSet/Contains: {0}ms", watch.ElapsedMilliseconds);
}

RÉSULTATS

***************************************
***** ContainsExistsAnyShortRange *****
***************************************
List/Contains: 65ms
List/Exists: 106ms
List/Any: 222ms
Array/Contains: 20ms
Arrays do not have Exists
Array/Any: 281ms
HashSet/Contains: 0ms
***************************************
********* ContainsExistsAny ***********
***************************************
List/Contains: 120522ms
List/Exists: 250445ms
List/Any: 653530ms
Array/Contains: 40801ms
Arrays do not have Exists
Array/Any: 522371ms
HashSet/Contains: 3ms
51
wertzui

Il est à noter que cette comparaison est un peu injuste puisque le Array la classe ne possède pas le Contains() méthode, il utilise une méthode d'extension pour IEnumerable<T> via un séquentiel Enumerator par conséquent, il n'est pas optimisé pour Array instances; d'un autre côté, HashSet<T> a sa propre implémentation entièrement optimisée pour toutes les tailles.

Pour comparer équitablement, vous pouvez utiliser la méthode statique int Array.IndexOf() qui est mis en œuvre pour Array cas, même si elle utilise un for boucle légèrement plus efficace qu'un Enumerator.

Cela dit, la performance de HashSet<T>.Contains() est similaire à la Array.IndexOf() pour de petits ensembles de, je dirais, jusqu'à 5 éléments et beaucoup plus efficace pour les grands ensembles.

4
LuckyBrain