سؤال

هذا هو مجرد سؤال لإرضاء فضولي.ولكن بالنسبة لي فإنه من المثير للاهتمام.

كتبت هذا القليل بسيطة المعيار.ويدعو 3 أنواع من Regexp التنفيذ في ترتيب عشوائي بضعة آلاف المرات:

أساسا أنا استخدم نفس النمط ولكن بطرق مختلفة.

  1. العادية الخاصة بك دون أي RegexOptions.بدءا .NET 2.0 هذه لا تحصل مؤقتا.ولكن يجب أن يكون "مؤقتا" لأنه هو الذي عقد في جميلة نطاق عالمي وليس إعادة تعيين.

  2. مع RegexOptions.Compiled

  3. مع دعوة إلى ثابت Regex.Match(pattern, input) التي لا تحصل مؤقتا في .NET 2.0

هنا هو رمز:

static List<string> Strings = new List<string>();        
static string pattern = ".*_([0-9]+)\\.([^\\.])$";

static Regex Rex = new Regex(pattern);
static Regex RexCompiled = new Regex(pattern, RegexOptions.Compiled);

static Random Rand = new Random(123);

static Stopwatch S1 = new Stopwatch();
static Stopwatch S2 = new Stopwatch();
static Stopwatch S3 = new Stopwatch();

static void Main()
{
  int k = 0;
  int c = 0;
  int c1 = 0;
  int c2 = 0;
  int c3 = 0;

  for (int i = 0; i < 50; i++)
  {
    Strings.Add("file_"  + Rand.Next().ToString() + ".ext");
  }
  int m = 10000;
  for (int j = 0; j < m; j++)
  {
    c = Rand.Next(1, 4);

    if (c == 1)
    {
      c1++;
      k = 0;
      S1.Start();
      foreach (var item in Strings)
      {
        var m1 = Rex.Match(item);
        if (m1.Success) { k++; };
      }
      S1.Stop();
    }
    else if (c == 2)
    {
      c2++;
      k = 0;
      S2.Start();
      foreach (var item in Strings)
      {
        var m2 = RexCompiled.Match(item);
        if (m2.Success) { k++; };
      }
      S2.Stop();
    }
    else if (c == 3)
    {
      c3++;
      k = 0;
      S3.Start();
      foreach (var item in Strings)
      {
        var m3 = Regex.Match(item, pattern);
        if (m3.Success) { k++; };
      }
      S3.Stop();
    }
  }

  Console.WriteLine("c: {0}", c1);
  Console.WriteLine("Total milliseconds: " + (S1.Elapsed.TotalMilliseconds).ToString());
  Console.WriteLine("Adjusted milliseconds: " + (S1.Elapsed.TotalMilliseconds).ToString());

  Console.WriteLine("c: {0}", c2);
  Console.WriteLine("Total milliseconds: " + (S2.Elapsed.TotalMilliseconds).ToString());
  Console.WriteLine("Adjusted milliseconds: " + (S2.Elapsed.TotalMilliseconds*((float)c2/(float)c1)).ToString());

  Console.WriteLine("c: {0}", c3);
  Console.WriteLine("Total milliseconds: " + (S3.Elapsed.TotalMilliseconds).ToString());
  Console.WriteLine("Adjusted milliseconds: " + (S3.Elapsed.TotalMilliseconds*((float)c3/(float)c1)).ToString());
}

في كل مرة أنا أسميها النتيجة على غرار:

    Not compiled and not automatically cached:
    Total milliseconds: 6185,2704
    Adjusted milliseconds: 6185,2704

    Compiled and not automatically cached:
    Total milliseconds: 2562,2519
    Adjusted milliseconds: 2551,56949184038

    Not compiled and automatically cached:
    Total milliseconds: 2378,823
    Adjusted milliseconds: 2336,3187176891

لذلك فإنه يوجد لديك.ليس كثيرا, ولكن حوالي 7-8 ٪ الفرق.

ليس فقط سر.أنا لا يمكن أن يفسر لماذا الطريقة الأولى سيكون أبطأ بكثير لانه لن نعيد تقييم عقد في عالمي متغير ثابت.

بالمناسبة هذا هو على .Net framework 3.5 و أحادية 2.2 والتي تتصرف بالضبط نفس الشيء.على نظام التشغيل ويندوز.

أي أفكار ، لماذا جمعت البديل حتى تقع وراء ؟

EDIT1:

بعد تحديد رمز النتائج الآن تبدو مثل هذا:

    Not compiled and not automatically cached:
    Total milliseconds: 6456,5711
    Adjusted milliseconds: 6456,5711

    Compiled and not automatically cached:
    Total milliseconds: 2668,9028
    Adjusted milliseconds: 2657,77574842168

    Not compiled and automatically cached:
    Total milliseconds: 6637,5472
    Adjusted milliseconds: 6518,94897724836

الذي يلغي كل الأسئلة الأخرى كذلك.

شكرا على الإجابات.

هل كانت مفيدة؟

المحلول

في Regex.مباراة الإصدار كنت تبحث عن الإدخال في النموذج.حاول مبادلة المعلمات في جميع أنحاء.

var m3 = Regex.Match(pattern, item); // Wrong
var m3 = Regex.Match(item, pattern); // Correct

نصائح أخرى

لاحظت سلوك مماثل.أنا أيضا أتساءل لماذا النسخة المترجمة ستكون أبطأ, ولكن لاحظت أن فوق عدد معين من المكالمات النسخة المترجمة أسرع.لذا بحثت في عاكس قليلا و لاحظت أن جمعت Regex, لا يزال هناك القليل من الإعداد الذي يتم تنفيذه على المكالمة الأولى (على وجه التحديد ، إنشاء مثيل المناسبة RegexRunner كائن).

في اختبار, لقد وجدت أنه إذا انتقلت إلى كل من منشئ أولية رمي بعيدا الدعوة إلى regex خارج بدء الموقت ، جمعت regex فاز بغض النظر عن كيفية العديد من التكرارات ركضت.


بالمناسبة, التخزين المؤقت هذا الإطار تفعل عند استخدام ثابت Regex الطرق الأمثل التي يحتاج فقط عند استخدام ثابت Regex الأساليب.وهذا لأن كل دعوة إلى ثابت Regex يخلق طريقة جديدة Regex الكائن.في Regex فئة منشئ يجب تحليل نمط.التخزين المؤقت يسمح اللاحقة المكالمات من ثابت Regex طرق إعادة استخدام RegexTree تحليل من المكالمة الأولى ، وبالتالي تجنب تحليل الخطوة.

عند استخدام أساليب المثال على واحد Regex كائن ، ثم هذه ليست قضية.تحليل لا يزال يتم القيام بها إلا مرة واحدة (عند إنشاء كائن).وبالإضافة إلى ذلك, يمكنك الحصول على تجنب تشغيل كافة التعليمات البرمجية الأخرى في منشئ ، وكذلك تخصيص كتلة (اللاحقة جمع القمامة).

مارتن براون لاحظت أن كنت عكس حجج ثابتة Regex دعوة (صيد ثمين ، مارتن).أعتقد أنك سوف تجد أنه إذا كنت أصلح هذا على سبيل المثال (لا-المترجمة) regex فوز ثابت المكالمات في كل مرة.يجب أن تجد أيضا أنه في ضوء النتائج التي توصلت إليها أعلاه ، جمعت سبيل المثال سوف تغلب على غير المترجمة أيضا.

ولكن:عليك قراءة جيف أتوود بعد على تجميع regexes قبل أن تذهب عمياء تطبيق هذا الخيار على كل التعابير المنطقية التي تقوم بإنشائها.

إذا كنت باستمرار تطابق نفس السلسلة باستخدام نفس النمط ، وهذا قد يفسر لماذا نسخة مخبأة هو أسرع قليلا من النسخة المترجمة.

هذا من الوثائق ؛

https://msdn.microsoft.com/en-us/library/gg578045(v=مقابل 110).aspx

عندما ثابت التعبير العادي الطريقة تسمى العادية التعبير لا يمكن أن تكون موجودة في ذاكرة التخزين المؤقت ، التعبير العادي المحرك يحول التعبير العادية إلى مجموعة من العمليات رموز و مخازن لهم في ذاكرة التخزين المؤقت.ثم يحول هذه العملية إلى رموز MSIL حتى أن التحويل البرمجي JIT يمكن تنفيذها. تفسير العادية عبارات تقلل من وقت بدء التشغيل في تكلفة أبطأ وقت التنفيذ.بسبب هذا, هم استخدام أفضل عندما التعبير العادي هو يستخدم في عدد قليل من المكالمات الأسلوب, أو إذا كان العدد الدقيق المكالمات العادية التعبير أساليب غير معروف ولكن من المتوقع أن يكون الصغيرة.حيث بلغ عدد المكالمات الأسلوب يزيد من كسب الأداء من تخفيض وقت بدء التشغيل هو فاق من بطء التنفيذ السرعة.

وعلى النقيض من تفسير التعابير العادية ، جمعت العادية تعبيرات زيادة وقت بدء التشغيل ولكن تنفيذ الفردية نمط مطابقة أساليب أسرع.ونتيجة لذلك ، فإن أداء فائدة الذي ينتج عن تجميع التعبير العادي يزيد في نسبة إلى عدد من التعبير العادي أساليب يسمى.


لتلخيص, نحن ننصح باستخدام تفسير التعابير العادية عند استدعاء التعبير العادي أساليب محددة التعبير العادي نادرا نسبيا.

يجب عليك استخدام جمعت التعبيرات العادية عند استدعاء العادية التعبير أساليب محددة التعبير العادي نسبيا في كثير من الأحيان.


كيفية اكتشافه ؟

بالضبط العتبة التي أبطأ التنفيذ بسرعة تفسير التعبيرات العادية تفوق المكاسب من انخفاض وقت بدء التشغيل ، أو العتبة التي أبطأ أوقات بدء التشغيل من جمعت التعبيرات العادية تفوق المكاسب من أسرع السرعة في التنفيذ, من الصعب تحديد.ذلك يعتمد على مجموعة متنوعة من العوامل ، بما في ذلك تعقيد التعبير العادي ، بيانات محددة أن العمليات. لتحديد ما إذا كان تفسير أو جمعت التعبيرات العادية نقدم أفضل أداء خاصة تطبيق السيناريو ، يمكنك استخدام ساعة توقيت فئة قارن تنفيذها مرات.


جمعت التعبيرات العادية:

ونحن ننصح ترجمة التعبيرات العادية إلى الجمعية في الحالات التالية:

  1. إذا كنت عنصر المطور الذي يريد لإنشاء مكتبة قابلة لإعادة الاستخدام التعبيرات العادية.
  2. إذا كنت تتوقع التعبير العادي هو نمط مطابقة الطرق أن يسمى غير محدد عدد مرات -- في أي مكان من مرة أو مرتين الآلاف أو عشرات الآلاف من المرات.على عكس تجميع أو تفسير التعابير العادية ، التعبيرات العادية التي جمعت فصل الجمعيات نقدم الأداء الذي هو ثابت بغض النظر عدد المكالمات الأسلوب.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top