Reputation: 1453
I don't like premature optimization but I was curious while doing a simple task so I added a stopwatch. I don't understand how the difference can be so big.
Array of strings (7 characters) each [richtextbox.text].
Length of array: 5500 elements.
Foreach Elapsed Time: 0.0015 seconds
For Elapsed Time: 9.757 seconds
For:
if (chkLineBreaks.Checked)
{
for (int i = 0; i < txtInput.Lines.Length; i++)
{
outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
}
}
Foreach:
foreach (var line in txtInput.Lines)
{
outputStringBuilder.Append($@"'{line}',");
if (chkLineBreaks.Checked)
outputStringBuilder.AppendLine();
}
From what I've read the difference should be negligible and the For would slightly faster.
Even more, the foreach has a condition in each iteration (unless it is being 'hoisted' up before the loop.
What is going on here?
Edit: I've changed the foreach code to:
int i = 0;
foreach (var line in txtInput.Lines)
{
outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
i++;
}
So it is now doing the same thing. It is taking 4.625 seconds.. still about half of the time for the FOR
Also I know that I can extract the array outside the loop but this is not what I am testing here :)
Edit #2: This is the whole code for that section:
Stopwatch sw = new Stopwatch();
sw.Start();
// for (int i = 0; i < txtInput.Lines.Length; i++)
// {
// outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
// }
int i = 0;
foreach (var line in txtInput.Lines)
{
outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
i++;
}
MessageBox.Show(sw.Elapsed.ToString());
Upvotes: 0
Views: 101
Reputation: 29244
The compiled code sees very little difference between a for
and a foreach
statement when traversing an array (or list).
Consider this simple code the writes out a list of strings three different ways:
class Program
{
static void Main(string[] args)
{
var list = Enum.GetNames(typeof(System.UriComponents));
// 1. for each
foreach (var item in list)
{
Console.WriteLine(item);
}
// 2. for loop
for (int i = 0; i<list.Length; i++)
{
Console.WriteLine(list[i]);
}
// 3. LINQ
Console.WriteLine(string.Join(Environment.NewLine, list));
}
}
Now look at the MSIL
compiled code, translated back into C#
using ILSpy
or DotNetPeek
.
// ConsoleApplication1.Program
private static void Main(string[] args)
{
string[] list = Enum.GetNames(typeof(UriComponents));
string[] array = list;
for (int j = 0; j < array.Length; j++)
{
string item = array[j];
Console.WriteLine(item);
}
for (int i = 0; i < list.Length; i++)
{
Console.WriteLine(list[i]);
}
Console.WriteLine(string.Join(Environment.NewLine, list));
}
See the two for loops. The foreach
statement became a for
loop by the compiler. As far as the string.Join()
statement it calls the SZArrayEnumerator
which holds a reference to the array, and the current index value. At each .MoveNext()
call the index is incremented and a new value returned. Basically, it is equivalent to the following:
int i = 0;
while (i<list.Length)
{
Console.WriteLine(list[i]);
i++;
}
Upvotes: 1
Reputation: 23819
The issue is that txtInput.Lines
is executing many times (once per line) in your for
loop (due to use of txtInput.Lines[i]
). So for every line of the file you are saying 'OK, please parse this textbox into multiple lines - and then get me the nth line' - and the parsing is the killer bit.
For a fairer comparison:
if (chkLineBreaks.Checked)
{
var lines = txtInput.Lines;
for (int i = 0; i < lines.Length; i++)
{
outputStringBuilder.Append($@"'{lines[i]}',");
}
}
this way the Lines
call is done only once (i.e. equivalent to the foreach
scenario).
One way to spot these kinds of issues is to compare the timings. The slow one is about 6K slower than the fast one, and you have 5.5K entries. Since 5.5K and 6K are very similar numbers, it may prompt you to think 'am I doing something in the loop that I really shouldn't?'
Upvotes: 3