dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.25k stars 4.73k forks source link

Reflection performance in NativeAOT #66620

Open AhmedZero opened 2 years ago

AhmedZero commented 2 years ago

Description

when I use the following example without NativeAOT, it's very fast, but with NativeAOT, I found it's very slow.

using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Reflection;
namespace MyApp 
{

 public static class Utils
    {
        public static string GetDescription(this Enum value)
        {
            FieldInfo field = value.GetType().GetField(value.ToString());

            DescriptionAttribute attribute
                    = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))
                        as DescriptionAttribute;

            return attribute == null ? value.ToString() : attribute.Description;
        }
    }
internal unsafe class Program
    {
        public enum Item
        {
            [Description("Zero")]
            Useless,
            [Description("Test--1")]
            Test1,
            [Description("Test--2")]
            Test2,
            [Description("Test--3")]
            Test3,
            [Description("Test--4")]
            Test4,
            [Description("Test--5")]
            Test5,
            [Description("Test--6")]
            Test6,
            [Description("Test--7")]
            Test7,
            [Description("Test--8")]
            Test8,
            [Description("Test--9")]
            Test9,
            [Description("Test--10")]
            Test10,
            [Description("Test--11")]
            Test11,
            [Description("Test--12")]
            Test12,
            [Description("Test--13")]
            Test13,
            [Description("Test--14")]
            Test14,
            [Description("Test--15")]
            Test15,
            [Description("Test--16")]
            Test16,
            [Description("Test--17")]
            Test17,
            [Description("Test--18")]
            Test18,
            [Description("Test--19")]
            Test19,
            [Description("Test--20")]
            Test20,
            [Description("Test--21")]
            Test21,
            [Description("Test--22")]
            Test22,
            [Description("Test--23")]
            Test23,
            [Description("Test--24")]
            Test24,
            [Description("Test--25")]
            Test25,
            [Description("Test--26")]
            Test26,
            [Description("Test--27")]
            Test27,
            [Description("Test--28")]
            Test28,
            [Description("Test--29")]
            Test29,
            [Description("Test--30")]
            Test30,
            [Description("Test--31")]
            Test31,
            [Description("Test--32")]
            Test32,
            [Description("Test--33")]
            Test33,
            [Description("Test--34")]
            Test34,
            [Description("Test--35")]
            Test35,
            [Description("Test--36")]
            Test36
        }

        static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            List<string> test1 = new List<string>();
            for (int i = 0; i < 100000; i++)
            {
                test1.Add(Item.Test36.GetDescription());
            }
            sw.Stop();
            Console.WriteLine("First Example: " + sw.ElapsedTicks);
            sw.Restart();
            sw.Start();
            List<string> test2 = new List<string>();
            for (int i = 0; i < 100000; i++)
            {
                test2.Add("test--36");
            }
            sw.Stop();
            Console.WriteLine("Second Example: " + sw.ElapsedTicks);

            Console.ReadKey();
        }
   }
}

Output

Without NativeAOT

First Example: 1711806
Second Example: 10042

With NativeAOT

First Example: 5760899
Second Example: 20363

Configuration

Win-x64 .NET 6

dotnet-issue-labeler[bot] commented 2 years ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

hez2010 commented 2 years ago

There's some performance degrade but not that huge. For performance test you need to use BenchmarkDotNet to get accurate result, rather than using a StopWatch around a loop iteration whose result can be affected by many other factors.

I ran it with BenchmarkDotNet and got this result:

Method Toolchain Mean Error StdDev
Example1 .NET 6.0 107.742 ms 2.0753 ms 3.1062 ms
Example2 .NET 6.0 1.033 ms 0.0191 ms 0.0267 ms
Example1 NativeAOT 155.764 ms 3.4181 ms 5.1160 ms
Example2 NativeAOT 1.123 ms 0.0222 ms 0.0325 ms

List itself has no performance difference between JIT and NativeAOT, while reflection in NativeAOT is a bit slower than JIT.

AhmedZero commented 2 years ago

I don't know benchmark.net works with NativeAOT and my program has those conditions and I program it like that. when the program add a lot of elements to the list, the performance becomes bad.

jkotas commented 2 years ago

List itself has no performance difference between JIT and NativeAOT, while reflection in NativeAOT is a bit slower than JIT.

Yes, this is what I am seeing as well.

AhmedZero commented 2 years ago
NativeAOT Method Mean Error StdDev
Example1 175.341 ms 11.0707 ms 0.6068 ms
Example2 1.002 ms 0.2061 ms 0.0113 ms

Without NativeAOT

Method Mean Error StdDev
Example1 124.3967 ms 2.4448 ms 3.0919 ms
Example2 0.8375 ms 0.0163 ms 0.0167 ms

you're correct @hez2010