orm 里使用Emit

本文通过在SQLite数据库中进行大量数据查询实验,比较了使用Emit、反射和直接赋值三种方式的效率。结果显示,直接赋值最快,其次是Emit,反射最慢,具体表现为直接赋值耗时1604毫秒,Emit耗时1745毫秒,而反射则耗时2725毫秒。

比较一下Emit的赋值,反射赋值和直接赋值的效率

namespace Assignment
{
    class Program
    {
        static SQLiteConnection conn;
        static string dbStr = "test.db";
        static void Main(string[] args)
        {
            CreateDB();
            List<Book> books = new List<Book>();
            for (int i = 0; i < 1000000; i++)
            {
                books.Add(new Book());
            }
            Insert(books);
            Insert(new Book());

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            var bs = Query<Book>();
            stopwatch.Stop();
            Console.WriteLine(stopwatch.ElapsedMilliseconds);

            stopwatch.Restart();
            var bs1 = QueryEmit<Book>();
            stopwatch.Stop();
            Console.WriteLine(stopwatch.ElapsedMilliseconds);

            stopwatch.Restart();
            var bs2 = Query();
            stopwatch.Stop();
            Console.WriteLine(stopwatch.ElapsedMilliseconds);

            Console.ReadLine();
        }

        static void CreateDB()
        {
            SQLiteConnection.CreateFile(dbStr);
            conn = new SQLiteConnection($"Data Source={dbStr};Version=3;");
            conn.Open();

            string sql = "create table book (id int,name varchar(20), price double)";
            SQLiteCommand command = new SQLiteCommand(sql, conn);
            command.ExecuteNonQuery();
        }
        static void Insert(Book book)
        {
            string sql = "insert into book values(@id,@name,@price)";
            SQLiteCommand command = new SQLiteCommand(sql, conn);
            command.Parameters.AddWithValue("id", book.Id);
            command.Parameters.AddWithValue("name", book.Name);
            command.Parameters.AddWithValue("price", book.Price);

            command.ExecuteNonQuery();
        }
        static void Insert(IList<Book> books)
        {
            string sql = "insert into book values(@id,@name,@price)";
            var trans = conn.BeginTransaction();
            SQLiteCommand command = new SQLiteCommand(sql, conn, trans);
            foreach (var book in books)
            {
                command.Parameters.Clear();
                command.Parameters.AddWithValue("id", book.Id);
                command.Parameters.AddWithValue("name", book.Name);
                command.Parameters.AddWithValue("price", book.Price);

                command.ExecuteNonQuery();
            }
            trans.Commit();
        }

        static List<Book> Query()
        {
            List<Book> result = new List<Book>();
            string sql = "select * from book";
            SQLiteCommand command = new SQLiteCommand(sql, conn);
            var reader = command.ExecuteReader();
            while (reader.Read())
            {
                Book book = new Book();
                book.Id = (int)reader.GetValue(0);
                book.Name = (string)reader.GetValue(1);
                book.Price = (double)reader.GetValue(2);
                result.Add(book);
            }
            return result;
        }

        static List<T> Query<T>()
        {
            List<T> result = new List<T>();
            string sql = "select * from book";
            SQLiteCommand command = new SQLiteCommand(sql, conn);
            Type type = typeof(T);
            var reader = command.ExecuteReader();
            var readerMap = new ReaderMap(reader);
            var propertyMap = new PropertyMap(readerMap, type);
            while (reader.Read())
            {
                T r = (T)Activator.CreateInstance(type);
                int len = reader.FieldCount;
                for(int i = 0; i < len; i++)
                {
                    propertyMap[i].SetValue(r, reader.GetValue(i));
                }
                result.Add(r);
            }

            return result;
        }
        
        static List<T> QueryEmit<T>()
        {
            Type type = typeof(T);

            List<T> result = new List<T>();
            string sql = "select * from book";
            SQLiteCommand command = new SQLiteCommand(sql, conn);
            var reader = command.ExecuteReader();
            var readerMap = new ReaderMap(reader);
            var propertyMap = new PropertyMap(readerMap, type);

            var func = GetSetter<T>(reader, propertyMap);

            while (reader.Read())
            {
                T r = (T)func(reader);
                result.Add(r);
            }

            return result;
        }

        static Func<SQLiteDataReader, object> GetSetter<T>(SQLiteDataReader reader, PropertyMap map)
        {
            Type bookType = typeof(T);
            var constructor = bookType.GetConstructors().FirstOrDefault();
            DynamicMethod setter = new DynamicMethod("setbook", bookType, new Type[] { typeof(SQLiteDataReader) });
            setter.DefineParameter(0, ParameterAttributes.In, "reader");

            var iLGenerator = setter.GetILGenerator();

            iLGenerator.DeclareLocal(bookType); //Ldloc_0 book
            iLGenerator.DeclareLocal(typeof(object));//Ldloc_1 reader.GetValue

            iLGenerator.Emit(OpCodes.Nop);
            iLGenerator.Emit(OpCodes.Newobj, constructor);
            iLGenerator.Emit(OpCodes.Stloc_0);
            iLGenerator.Emit(OpCodes.Nop);

            var getM = typeof(DbDataReader).GetMethod("GetValue");

            int len = map.Count;
            for (int i = 0; i < len; i++)
            {
                ///读数据
                iLGenerator.Emit(OpCodes.Ldarg_0);
                iLGenerator.Emit(OpCodes.Ldc_I4, i);
                iLGenerator.Emit(OpCodes.Callvirt, getM);
                iLGenerator.Emit(OpCodes.Stloc_1);
                iLGenerator.Emit(OpCodes.Nop);

                var tp = map[i];
                iLGenerator.Emit(OpCodes.Ldloc_0);
                iLGenerator.Emit(OpCodes.Ldloc_1);
                if (tp.PropertyType.IsValueType)
                    iLGenerator.Emit(OpCodes.Unbox_Any, tp.PropertyType);
                else
                    iLGenerator.Emit(OpCodes.Castclass, tp.PropertyType);

                var mt = tp.GetSetMethod();

                iLGenerator.Emit(OpCodes.Callvirt, mt);
                iLGenerator.Emit(OpCodes.Nop);
            }

            iLGenerator.Emit(OpCodes.Ldloc_0);
            iLGenerator.Emit(OpCodes.Ret);

            return (Func<SQLiteDataReader, object>)setter.CreateDelegate(typeof(Func<SQLiteDataReader,object>));
        }

        static Book QueryBook(SQLiteDataReader reader)
        {
            Book book = new Book();
            book.Id = (int)reader.GetValue(0);
            book.Name = (string)reader.GetValue(1);
            book.Price = (double)reader.GetValue(2);
            return book;
        }
    }
    public class PropertyMap
    {
        private PropertyInfo[] properties = null;
        public PropertyMap(ReaderMap readerMap,Type type)
        {
            int len = readerMap.Count;
            Count = len;
            var ps = type.GetProperties();
            properties = new PropertyInfo[len];
            for(int i = 0; i < len; i++)
            {
                var readerItem = readerMap[i];
                var tp = ps.FirstOrDefault(p => p.Name.ToUpper() == readerItem.Name.ToUpper());
                if (tp != null)
                {
                    if (tp.PropertyType.IsAssignableFrom(readerItem.Type))
                    {
                        properties[i] = tp;
                    }
                }                
            }
        }
        public PropertyInfo this[int i]
        {
            get
            {
                return properties[i];
            }
        }
        public int Count { get; private set; }
    }
    public class ReaderMap
    {
        private ReaderItem[] items = null; 
        public ReaderMap(SQLiteDataReader reader)
        {
            int len = reader.FieldCount;
            Count = len;
            items = new ReaderItem[len];
            for(int i = 0; i < len; i++)
            {
                items[i] = new ReaderItem
                {
                    Id = i,
                    Name = reader.GetName(i),
                    Type = reader.GetFieldType(i)
                };
            }
        }
        public int Count
        {
            get;
            private set;
        }
        public ReaderItem this[int i]
        {
            get
            {
                return items[i];
            }
        }
    }
    public class ReaderItem
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Type Type { get; set; }
    }
    public class Book
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Price { get; set; }
        public Book()
        {
            Id = 0;
            Name = "Name";
            Price = 11.9;
        }

        public override string ToString()
        {
            return $"Id; {Id}\tName: {Name}\tPrice: {Price}";
        }
    }
}

输出结果为

反射时间2725

Emit 时间1745

直接赋值1604

恩,就是这样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值