locked
foreach loop with action inside the braces RRS feed

  • Question

  • User1737887415 posted

    Hi there.

    I ran a CPU diagnostic for the following cods but didn't see a huge difference between the two.
    Is some one can tell me what is the better way of doing it or if it matter at all (based on the diagnostic i made)
    I guess the second way is the better one,, but why couldn't see a stable difference in the diagnostics.

    Thanks.

    1. 
    Regex reg = new Regex();
    foreach (Match m in reg.Matches(_content))  {XXX}

    2.
    MatchCollection matches = reg.Matches(_content);
    foreach (Match m in matches)  {XXX}

    Monday, August 11, 2014 11:59 AM

Answers

  • User281315223 posted

    Defining the collection within the foreach loop shouldn't execute it for each iteration of the loop itself. 

    Additionally, if anyone was interested in what the IL looked like for each type of operation, you can see it below :

    Option 1 : "Inline Declaration"

    System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@"[^\d]");
    foreach (Match m in reg.Matches("This is a test"))
    {
    	// Do something here
    	Console.WriteLine(m.Value);
    }

    yields :

    IL_0001:  ldstr       "[^\d]"
    IL_0006:  newobj      System.Text.RegularExpressions.Regex..ctor
    IL_000B:  stloc.0     // reg
    IL_000C:  nop         
    IL_000D:  ldloc.0     // reg
    IL_000E:  ldstr       "This is a test"
    IL_0013:  callvirt    System.Text.RegularExpressions.Regex.Matches
    IL_0018:  callvirt    System.Text.RegularExpressions.MatchCollection.GetEnumerator
    IL_001D:  stloc.2     // CS$5$0000
    IL_001E:  br.s        IL_003A
    IL_0020:  ldloc.2     // CS$5$0000
    IL_0021:  callvirt    System.Collections.IEnumerator.get_Current
    IL_0026:  castclass   System.Text.RegularExpressions.Match
    IL_002B:  stloc.1     // m
    IL_002C:  nop         
    IL_002D:  ldloc.1     // m
    IL_002E:  callvirt    System.Text.RegularExpressions.Capture.get_Value
    IL_0033:  call        System.Console.WriteLine
    IL_0038:  nop         
    IL_0039:  nop         
    IL_003A:  ldloc.2     // CS$5$0000
    IL_003B:  callvirt    System.Collections.IEnumerator.MoveNext
    IL_0040:  stloc.3     // CS$4$0001
    IL_0041:  ldloc.3     // CS$4$0001
    IL_0042:  brtrue.s    IL_0020
    IL_0044:  leave.s     IL_0060
    IL_0046:  ldloc.2     // CS$5$0000
    IL_0047:  isinst      System.IDisposable
    IL_004C:  stloc.s     04 // CS$0$0002
    IL_004E:  ldloc.s     04 // CS$0$0002
    IL_0050:  ldnull      
    IL_0051:  ceq         
    IL_0053:  stloc.3     // CS$4$0001
    IL_0054:  ldloc.3     // CS$4$0001
    IL_0055:  brtrue.s    IL_005F
    IL_0057:  ldloc.s     04 // CS$0$0002
    IL_0059:  callvirt    System.IDisposable.Dispose
    IL_005E:  nop         
    IL_005F:  endfinally  

    Option 2: "Prior Declaration"

    System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@"[^\d]");
    MatchCollection matches = reg.Matches("This is a test");
    foreach (Match m in matches)
    {
    	// Do something here
    	Console.WriteLine(m.Value);
    }

    yields :

    IL_0001:  ldstr       "[^\d]"
    IL_0006:  newobj      System.Text.RegularExpressions.Regex..ctor
    IL_000B:  stloc.0     // reg
    IL_000C:  ldloc.0     // reg
    IL_000D:  ldstr       "This is a test"
    IL_0012:  callvirt    System.Text.RegularExpressions.Regex.Matches
    IL_0017:  stloc.1     // matches
    IL_0018:  nop         
    IL_0019:  ldloc.1     // matches
    IL_001A:  callvirt    System.Text.RegularExpressions.MatchCollection.GetEnumerator
    IL_001F:  stloc.3     // CS$5$0000
    IL_0020:  br.s        IL_003C
    IL_0022:  ldloc.3     // CS$5$0000
    IL_0023:  callvirt    System.Collections.IEnumerator.get_Current
    IL_0028:  castclass   System.Text.RegularExpressions.Match
    IL_002D:  stloc.2     // m
    IL_002E:  nop         
    IL_002F:  ldloc.2     // m
    IL_0030:  callvirt    System.Text.RegularExpressions.Capture.get_Value
    IL_0035:  call        System.Console.WriteLine
    IL_003A:  nop         
    IL_003B:  nop         
    IL_003C:  ldloc.3     // CS$5$0000
    IL_003D:  callvirt    System.Collections.IEnumerator.MoveNext
    IL_0042:  stloc.s     04 // CS$4$0001
    IL_0044:  ldloc.s     04 // CS$4$0001
    IL_0046:  brtrue.s    IL_0022
    IL_0048:  leave.s     IL_0066
    IL_004A:  ldloc.3     // CS$5$0000
    IL_004B:  isinst      System.IDisposable
    IL_0050:  stloc.s     05 // CS$0$0002
    IL_0052:  ldloc.s     05 // CS$0$0002
    IL_0054:  ldnull      
    IL_0055:  ceq         
    IL_0057:  stloc.s     04 // CS$4$0001
    IL_0059:  ldloc.s     04 // CS$4$0001
    IL_005B:  brtrue.s    IL_0065
    IL_005D:  ldloc.s     05 // CS$0$0002
    IL_005F:  callvirt    System.IDisposable.Dispose
    IL_0064:  nop         
    IL_0065:  endfinally  
    

    It looks like the second approach (defining the MatchCollection first) generates just two additional operations over the first "inline" approach. 

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, August 14, 2014 2:56 PM

All replies

  • User475983607 posted

    One and two are the same except 2 declares a MatchCollection object which points to reg.Matches(_content).  Two is more convenient if you're stepping through the code.

    Monday, August 11, 2014 12:17 PM
  • User281315223 posted

    This is primarily code-style and it can really vary from person to person. Logically, both of these should do the same thing (as your reg.Matches(_content) is going to return a MatchCollection).

    It's up to you if you prefer to create a variable to store them in or not. Creating a variable can provide you with a bit more freedom when it comes to debugging (as you could use the debugger to see the values in your MatchCollection) and it can also make your code a bit more readable (as "foreach Match m in matches" is more readable than "foreach Match m in reg.Matches(_content)").

    Again - these are both going to essentially do the same thing, it's just a matter of preference.

    Monday, August 11, 2014 12:38 PM
  • User1737887415 posted

    Hi,

    I know these two code parts will do the same job finally...
    My question, is the .NET running the regex matches again in each loop, so the performance is worst...?...
    Is the CPU works harder to finish a loop let say of 1000 matches...

    Thanks

    Wednesday, August 13, 2014 3:31 AM
  • User-1360095595 posted

    I believe no. 2 is more optimal because you don't have to execute the Matches() every time. I might be wrong. BUT, I would use method 2 for sure...

    Wednesday, August 13, 2014 3:35 AM
  • User753101303 posted

    Hi,

    No, reg.Matches(_content) doesn't run multiple time just because it follows the foreach statement. This is the loop body that runs multiple times.

    It would be interesting to look at the generated IL code but more likely when using #1, #2 is what the compiler does for you.

    Thursday, August 14, 2014 12:00 PM
  • User281315223 posted

    Defining the collection within the foreach loop shouldn't execute it for each iteration of the loop itself. 

    Additionally, if anyone was interested in what the IL looked like for each type of operation, you can see it below :

    Option 1 : "Inline Declaration"

    System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@"[^\d]");
    foreach (Match m in reg.Matches("This is a test"))
    {
    	// Do something here
    	Console.WriteLine(m.Value);
    }

    yields :

    IL_0001:  ldstr       "[^\d]"
    IL_0006:  newobj      System.Text.RegularExpressions.Regex..ctor
    IL_000B:  stloc.0     // reg
    IL_000C:  nop         
    IL_000D:  ldloc.0     // reg
    IL_000E:  ldstr       "This is a test"
    IL_0013:  callvirt    System.Text.RegularExpressions.Regex.Matches
    IL_0018:  callvirt    System.Text.RegularExpressions.MatchCollection.GetEnumerator
    IL_001D:  stloc.2     // CS$5$0000
    IL_001E:  br.s        IL_003A
    IL_0020:  ldloc.2     // CS$5$0000
    IL_0021:  callvirt    System.Collections.IEnumerator.get_Current
    IL_0026:  castclass   System.Text.RegularExpressions.Match
    IL_002B:  stloc.1     // m
    IL_002C:  nop         
    IL_002D:  ldloc.1     // m
    IL_002E:  callvirt    System.Text.RegularExpressions.Capture.get_Value
    IL_0033:  call        System.Console.WriteLine
    IL_0038:  nop         
    IL_0039:  nop         
    IL_003A:  ldloc.2     // CS$5$0000
    IL_003B:  callvirt    System.Collections.IEnumerator.MoveNext
    IL_0040:  stloc.3     // CS$4$0001
    IL_0041:  ldloc.3     // CS$4$0001
    IL_0042:  brtrue.s    IL_0020
    IL_0044:  leave.s     IL_0060
    IL_0046:  ldloc.2     // CS$5$0000
    IL_0047:  isinst      System.IDisposable
    IL_004C:  stloc.s     04 // CS$0$0002
    IL_004E:  ldloc.s     04 // CS$0$0002
    IL_0050:  ldnull      
    IL_0051:  ceq         
    IL_0053:  stloc.3     // CS$4$0001
    IL_0054:  ldloc.3     // CS$4$0001
    IL_0055:  brtrue.s    IL_005F
    IL_0057:  ldloc.s     04 // CS$0$0002
    IL_0059:  callvirt    System.IDisposable.Dispose
    IL_005E:  nop         
    IL_005F:  endfinally  

    Option 2: "Prior Declaration"

    System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@"[^\d]");
    MatchCollection matches = reg.Matches("This is a test");
    foreach (Match m in matches)
    {
    	// Do something here
    	Console.WriteLine(m.Value);
    }

    yields :

    IL_0001:  ldstr       "[^\d]"
    IL_0006:  newobj      System.Text.RegularExpressions.Regex..ctor
    IL_000B:  stloc.0     // reg
    IL_000C:  ldloc.0     // reg
    IL_000D:  ldstr       "This is a test"
    IL_0012:  callvirt    System.Text.RegularExpressions.Regex.Matches
    IL_0017:  stloc.1     // matches
    IL_0018:  nop         
    IL_0019:  ldloc.1     // matches
    IL_001A:  callvirt    System.Text.RegularExpressions.MatchCollection.GetEnumerator
    IL_001F:  stloc.3     // CS$5$0000
    IL_0020:  br.s        IL_003C
    IL_0022:  ldloc.3     // CS$5$0000
    IL_0023:  callvirt    System.Collections.IEnumerator.get_Current
    IL_0028:  castclass   System.Text.RegularExpressions.Match
    IL_002D:  stloc.2     // m
    IL_002E:  nop         
    IL_002F:  ldloc.2     // m
    IL_0030:  callvirt    System.Text.RegularExpressions.Capture.get_Value
    IL_0035:  call        System.Console.WriteLine
    IL_003A:  nop         
    IL_003B:  nop         
    IL_003C:  ldloc.3     // CS$5$0000
    IL_003D:  callvirt    System.Collections.IEnumerator.MoveNext
    IL_0042:  stloc.s     04 // CS$4$0001
    IL_0044:  ldloc.s     04 // CS$4$0001
    IL_0046:  brtrue.s    IL_0022
    IL_0048:  leave.s     IL_0066
    IL_004A:  ldloc.3     // CS$5$0000
    IL_004B:  isinst      System.IDisposable
    IL_0050:  stloc.s     05 // CS$0$0002
    IL_0052:  ldloc.s     05 // CS$0$0002
    IL_0054:  ldnull      
    IL_0055:  ceq         
    IL_0057:  stloc.s     04 // CS$4$0001
    IL_0059:  ldloc.s     04 // CS$4$0001
    IL_005B:  brtrue.s    IL_0065
    IL_005D:  ldloc.s     05 // CS$0$0002
    IL_005F:  callvirt    System.IDisposable.Dispose
    IL_0064:  nop         
    IL_0065:  endfinally  
    

    It looks like the second approach (defining the MatchCollection first) generates just two additional operations over the first "inline" approach. 

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, August 14, 2014 2:56 PM