سرعة Linq Expression Tree المترجمة

هنا في هذه المقالة في التعليقات لم يكن هناك نزاع ، ولكن بعض "عدم التقارب" في مقارنة سرعة IL Emit وشجرة التعبير Linq المترجمة.

هذه المقالة المصغرة هي رمز اختبار السرعة + نتائج تشغيل هذا الاختبار.

للاختبار ، تم تحديد رمز يتقاطع مع محتويات المقالة الأصلية - التسلسل إلى دفق بطول سلسلة ، ثم جميع بايتات سلسلة مشفرة في UTF-16 (Encoding.Unicode).

قد لا يكون رمز التسلسل نفسه هو الأمثل ، ولكنه قريب من ذلك إذا لم تستخدم بنيات غير آمنة.

الرمز في كلا التطبيقين هو نفسه ، كما يمكنك أن ترى من خلال فحص بناء تعبير لامدا.

لم أزعجني بتوليد IL من خلال Emit - الرمز الذي يجب أن يكون "مثاليًا" الذي كتبته للتو في C # في الطريقة الثابتة (في الواقع ، جميع طرق برنامج الاختبار ثابتة ، لأن هذا هو تطبيق وحدة تحكم بسيط للغاية) - هذا وتسمى هذه الطريقة الأصلية .

الطريقة الثانية للمقارنة هي تعبير Lambda الذي تم إنشاؤه عن طريق استدعاء طريقة Compile (ربما يمكن تحقيق زيادة في السرعة باستخدام CompileToMethod ، لكن هذا ليس دقيقًا) - تسمى هذه الطريقة أيضًا Expresisons .

مكافأة! لسبب ما ، تمت إضافة اختبار إضافي - استدعاء غير مباشر للطريقة المستخدمة في الاختبار الأصلي - عن طريق تعيين الطريقة إلى حقل مفوض ثابت - تسمى هذه الطريقة Native Dlgt .

في بداية التطبيق ، يتم إخراج كلتا الطريقتين ، بحيث يمكنك التأكد من أن الرمز يولد نفس البيانات بالضبط.

إذن ، هذا هو رمز التطبيق (كلها مرة واحدة):

قائمة
using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; namespace ExpressionSpeedTest { class Program { static void Main(string[] args) { InitExpression(); InitDelegateNative(); var inst = new TestClass { StringProp = "abcdefabcdef" }; byte[] buff1, buff2; using (var ms1 = new MemoryStream()) { SaveString(ms1, inst); buff1 = ms1.ToArray(); } using (var ms2 = new MemoryStream()) { DynamicMethod(ms2, inst); buff2 = ms2.ToArray(); } Console.WriteLine($"Native string: {string.Join("", buff1.Select(b => Encoding.Default.GetString(new[] { b })))}"); Console.WriteLine($"Expressions string: {string.Join("", buff2.Select(b => Encoding.Default.GetString(new[] { b })))}"); GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); GC.Collect(GC.MaxGeneration); TestNative(); GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); GC.Collect(GC.MaxGeneration); TestDelegateNative(); GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); GC.Collect(GC.MaxGeneration); TestExpressions(); GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); GC.Collect(GC.MaxGeneration); Console.ReadLine(); } private static void TestDelegateNative() { var inst = new TestClass { StringProp = "abcdefabcdef" }; using (var ms = new MemoryStream()) { var sw = new Stopwatch(); sw.Start(); for (var idx = 0; idx < loopLength; idx++) { SaveString(ms, inst); } sw.Stop(); Console.WriteLine($"Native Dlgt test: {sw.Elapsed}, {sw.ElapsedTicks} ticks"); } } private static void InitDelegateNative() { NativeDelegate = SaveString; } private static void InitExpression() { var intGetBytes = typeof(BitConverter).GetMethods(BindingFlags.Static | BindingFlags.Public) .Single(x => x.Name == nameof(BitConverter.GetBytes) && x.GetParameters()[0].ParameterType == typeof(int)); var stringGetBytes = typeof(Encoding).GetMethods(BindingFlags.Instance | BindingFlags.Public) .Single(x => x.Name == nameof(Encoding.GetBytes) && x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == typeof(string)); var unicodeProp = typeof(Encoding).GetProperty(nameof(Encoding.Unicode), BindingFlags.Static | BindingFlags.Public); var streamWrite = typeof(Stream).GetMethod(nameof(Stream.Write)); var streamPar = Expression.Parameter(typeof(Stream), "stream"); var instPar = Expression.Parameter(typeof(TestClass), "inst"); var intBuffVar = Expression.Variable(typeof(byte[]), "intBuff"); var strBuffVar = Expression.Variable(typeof(byte[]), "strBuff"); var expressionBody = Expression.Block( new[] { intBuffVar, strBuffVar }, Expression.Assign(intBuffVar, Expression.Call(null, intGetBytes, Expression.Property( Expression.Property( instPar, nameof(TestClass.StringProp)), nameof(string.Length)))), Expression.Assign(strBuffVar, Expression.Call(Expression.Property(null, unicodeProp), stringGetBytes, Expression.Property( instPar, nameof(TestClass.StringProp) ))), Expression.Call(streamPar, streamWrite, intBuffVar, Expression.Constant(0), Expression.Property(intBuffVar, nameof(Array.Length))), Expression.Call(streamPar, streamWrite, strBuffVar, Expression.Constant(0), Expression.Property(strBuffVar, nameof(Array.Length))) ); DynamicMethod = Expression.Lambda<Action<Stream, TestClass>>(expressionBody, streamPar, instPar).Compile(); } private const int loopLength = 10000000; private static Action<Stream, TestClass> DynamicMethod; private static Action<Stream, TestClass> NativeDelegate; private static void TestExpressions() { var inst = new TestClass { StringProp = "abcdefabcdef" }; using (var ms = new MemoryStream()) { var sw = new Stopwatch(); sw.Start(); for (var idx = 0; idx < loopLength; idx++) { DynamicMethod(ms, inst); } sw.Stop(); Console.WriteLine($"Expressions test: {sw.Elapsed}, {sw.ElapsedTicks} ticks"); } } private static void TestNative() { var inst = new TestClass { StringProp = "abcdefabcdef" }; using (var ms = new MemoryStream()) { var sw = new Stopwatch(); sw.Start(); for (var idx = 0; idx < loopLength; idx++) { SaveString(ms, inst); } sw.Stop(); Console.WriteLine($"Native test: {sw.Elapsed}, {sw.ElapsedTicks} ticks"); } } public static void SaveString(Stream stream, TestClass instance) { var intBuff = BitConverter.GetBytes(instance.StringProp.Length); var strBuff = Encoding.Unicode.GetBytes(instance.StringProp); stream.Write(intBuff, 0, intBuff.Length); stream.Write(strBuff, 0, strBuff.Length); } } class TestClass { public string StringProp { get; set; } } } 


وهنا نتائج الاختبار على التكوين التالي:

وحدة المعالجة المركزية
	 معالج Intel (R) Core (TM) i7-3770 بسرعة 3.40 جيجاهرتز

	 السرعة الأساسية: 3.90 جيجاهرتز
	 المقابس: 1
	 النوى: 4
	 المعالجات المنطقية: 8
	 المحاكاة الافتراضية: ممكّنة
	 ذاكرة التخزين المؤقت L1: 256 كيلوبايت
	 ذاكرة التخزين المؤقت L2: 1.0 ميغابايت
	 ذاكرة التخزين المؤقت L3: 8.0 ميغابايت

	 الاستفادة 8٪
	 السرعة 4.05 جيجاهرتز
	 وقت التشغيل 5: 00: 43: 01
	 العمليات 239
	 المواضيع 4092
	 يعالج 168774


الذاكرة
	 32.0 جيجابايت DDR3

	 السرعة: 1600 ميجا هرتز
	 الفتحات المستخدمة: 4 من 4
	 عامل الشكل: DIMM
	 الأجهزة المحجوزة: 42.5 ميغابايت

	 متاح 20.7 جيجابايت
	 نسخة مخبأة 20.1 جيجابايت
	 ارتكبت 13.4 / 36.7 غيغابايت
	 تجمع مقسم إلى صفحات 855 ميغابايت
	 تجمع غير مقسم إلى صفحات 442 ميغابايت
	 قيد الاستخدام (مضغوط) 11.2 جيجابايت (48.6 ميجابايت)

الهدف .Net Framework 4.7
نظام التشغيل Windows 10 Pro x64 1803 build 17134.48

لذلك ، النتائج الموعودة:

التحويل البرمجي في Debug ، بدون تحسين ، التشغيل بدون مصحح (Ctrl + F5):
اختبرالوقت (النطاق الزمني)الوقت (القراد)٪ الوقت
أصلي00: 00: 01.576065115760651101.935٪
Dlgt الأصلي00: 00: 01.546147815461478100٪
التعبيرات00: 00: 01.583545415835454102.4188٪

الترجمة في الإصدار ، مع التحسين ، تبدأ بدون مصحح (Ctrl + F5):
اختبرالوقت (النطاق الزمني)الوقت (القراد)٪ الوقت
أصلي00: 00: 01.3182291
13182291
100٪
Dlgt الأصلي00: 00: 01.3300925
13300925
100.8999٪
التعبيرات00: 00: 01.4871786
14871786
112.8164٪

يمكن تلخيص بعض النتائج:

  • تعمل شجرة التعبير المجمعة بشكل أبطأ بنسبة 1-2٪ في Debug و 10-12 في الإصدار ، وهو أمر جيد جدًا ، مع الأخذ في الاعتبار أن إنشاء التعليمات البرمجية من خلال Expression Tree في وقت التشغيل أسهل بكثير.
  • في وضع التصحيح ، لسبب ما ، يكون استدعاء الأسلوب غير المباشر من خلال المفوض أسرع من المكالمة المباشرة.

رقم المكافأة 2 تحت المفسد - تمثيل التصحيح لتعبيرات Lambda قبل التجميع:

لامدا
 .Lambda # Lambda1 <System.Action`2 [System.IO.Stream، ExpressionSpeedTest.TestClass]> (
     System.IO.Stream $ تيار ،
     ExpressionSpeedTest.TestClass $ inst) {
     .كتلة (
         System.Byte [] $ intBuff ،
         System.Byte [] $ strBuff) {
         $ intBuff = .Call System.BitConverter.GetBytes (($ inst.StringProp) .Length)؛
         $ strBuff = .Call (System.Text.Encoding.Unicode) .GetBytes ($ inst.StringProp) ؛
         . اتصل $ stream.Write (
             $ intBuff ،
             0
             $ intBuff.Length) ؛
         . اتصل $ stream.Write (
             $ strBuff ،
             0
             $ strBuff.Length)
     }}
 }}

Source: https://habr.com/ru/post/ar413561/


All Articles