Why is this List<>.IndexOf code so much faster than the List[i] and manual compare?

前端 未结 3 1761
无人共我
无人共我 2021-02-06 02:30

I\'m running AQTime on this piece of code, I found that .IndexOf takes 16% of the time vs close to 80% for the other piece... They appear to use the same IsEqual and other rout

3条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2021-02-06 03:02

    I made the following assumptions:

    • PointD is a struct
    • IndexOf is indeed slower than manually iterating the list

    You can speed up IndexOf by implementing the IEquatable interface:

    struct PointD : IEquatable
    {
        public int X;
        public int Y;
        public int Z;
    
        public bool Equals(PointD other)
        {
            return (this.X == other.X) &&
                    (this.Y == other.Y) &&
                    (this.Z == other.Z);
        }
    }
    

    Without implementing the IEquatable interface, IndexOf will compare the two PointD elements using ValueType.Equals(object other) which involves expensive boxing operations.

    The documentation of the IEquatable interface states:

    The IEquatable interface is used by generic collection objects such as Dictionary, List, and LinkedList when testing for equality in such methods as Contains, IndexOf, LastIndexOf, and Remove. It should be implemented for any object that might be stored in a generic collection.

    Here is my complete benchmark code:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    
    struct PointD 
    {
        public int X;
        public int Y;
        public int Z;
    }
    
    class PerfTest
    {
        List _pCoord3Points = new List();
    
        int checkPointIndexOf(PointD pt)
        {
            return _pCoord3Points.IndexOf(pt);  
        }
    
        int checkPointForBreak(PointD pt)
        {
            int retIndex = -1;
            for (int i = 0; i < _pCoord3Points.Count; i++)
            {
                PointD otherPt = _pCoord3Points[i];
                if ((pt.X == otherPt.X) &&
                    (pt.Y == otherPt.Y) &&
                    (pt.Z == otherPt.Z))
                    retIndex = i;
                break;
            }
            return retIndex;
        }
    
        void init()
        {
            for (int x = 0; x < 100; x++)
            {
                for (int y = 0; y < 10; y++)
                {
                    for (int z = 0; z < 10; z++)
                    {
                        PointD pt = new PointD() { X = x, Y = y, Z = z };
                        _pCoord3Points.Add(pt);
                    }
                }
            }
        }
    
        static void Main(string[] args)
        {
            PerfTest test = new PerfTest();
            test.init();
            Stopwatch sw = Stopwatch.StartNew();
            for (int x = 0; x < 100; x++)
            {
                for (int y = 0; y < 10; y++)
                {
                    for (int z = 0; z < 10; z++)
                    {
                        PointD pt = new PointD() { X = x, Y = y, Z = z };
                        test.checkPointIndexOf(pt);
                    }
                }
            }
            sw.Stop();
            Console.WriteLine(sw.Elapsed);
            sw = Stopwatch.StartNew();
            for (int x = 0; x < 100; x++)
            {
                for (int y = 0; y < 10; y++)
                {
                    for (int z = 0; z < 10; z++)
                    {
                        PointD pt = new PointD() { X = x, Y = y, Z = z };
                        test.checkPointForBreak(pt);
                    }
                }
            }
            sw.Stop();
            Console.WriteLine(sw.Elapsed);
        }
    }
    

    On Windows 7, x64, .NET 4.0 x64 build I get the following timings (approx):

    IndexOf:  00:00:04.8096623
    For Loop: 00:00:00.0014203
    

    When turning PointD into a class the timings change to

    IndexOf:  00:00:01.0703627
    For Loop: 00:00:00.0014190
    

    When using a struct PointD implementing IEquatable I get more "similar" results, but IndexOf is still slower (there is no significant difference when using a class now):

    IndexOf:  00:00:00.3904615
    For Loop: 00:00:00.0015218
    

提交回复
热议问题