Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - For-loop internals

a quick, simple question from me about for-loops.

Situation I'm currently writing some high-performance code when I suddenly was wondering how the for-loop actually behaves. I know I've stumbled across this before, but can't for the life of me find this info again :/

Still, my main concern was with the limiter. Say we have:

for(int i = 0; i < something.awesome; i++)
{
// Do cool stuff
}

Question Is something.awesome stored as an internal variable or is the loop constantly retrieving something.awesome to do the logic-check? Why I'm asking is of course because I need to loop through a lot of indexed stuff and I really don't want the extra function-call overhead for each pass.

However if something.awesome is only called once, then I'm going back under my happy rock! :)

like image 246
Robert Kaufmann Avatar asked Jul 30 '10 08:07

Robert Kaufmann


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr. Stroustroupe.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.

Is C programming hard?

C is more difficult to learn than JavaScript, but it's a valuable skill to have because most programming languages are actually implemented in C. This is because C is a “machine-level” language. So learning it will teach you how a computer works and will actually make learning new languages in the future easier.


4 Answers

You can use a simple sample program to check the behaviour:

using System;

class Program
{
    static int GetUpperBound()
    {
        Console.WriteLine("GetUpperBound called.");
        return 5;
    }

    static void Main(string[] args)
    {
        for (int i = 0; i < GetUpperBound(); i++)
        {
            Console.WriteLine("Loop iteration {0}.", i);
        }
    }
}

The output is the following:

GetUpperBound called. 
Loop iteration 0. 
GetUpperBound called. 
Loop iteration 1. 
GetUpperBound called. 
Loop iteration 2. 
GetUpperBound called. 
Loop iteration 3. 
GetUpperBound called. 
Loop iteration 4. 
GetUpperBound called.

The details of this behaviour are described in the C# 4.0 Language Specification, section 8.3.3 (You will find the spec inside C:\Program Files\Microsoft Visual Studio 10.0\VC#\Specifications\1033):

A for statement is executed as follows:

  • If a for-initializer is present, the variable initializers or statement expressions are executed in the order they are written. This step is only performed once.

  • If a for-condition is present, it is evaluated.

  • If the for-condition is not present or if the evaluation yields true, control is transferred to the embedded statement. When and if control reaches the end point of the embedded statement (possibly from execution of a continue statement), the expressions of the for-iterator, if any, are evaluated in sequence, and then another iteration is performed, starting with evaluation of the for-condition in the step above.

  • If the for-condition is present and the evaluation yields false, control is transferred to the end point of the for statement.

like image 110
Dirk Vollmar Avatar answered Sep 30 '22 01:09

Dirk Vollmar


If something.awesome is a field it is likely to be access each time round the loop as something in the body of the loop may update it. If the body of the loop is simple enough and does not call any methods (apart from methods the compiler inlines), then the compiler may be able to proved that it is safe to put the value of something.awesome in a register. Compiler writers used to go to a lot of afford to do this short of thing.

However these days it takes a very long time to access a value from main memory, but once the value has been read for the first time, it is coached by the CPU. Reading the value for a 2nd time from the CPU cache is a lot closer in speed to reading it from a register then reading it from main memory. It is not uncommon for a CPU cache to be hundreds of times faster than main memory.

Now if something.awesome is a property, then it is in effect a method call. The compiler will call the method each time round the loop. However if the property/method is only a few lines of code it may be inline by the compiler. Inlineing is when the compiler puts in a copy of the method code directly rather than calling the method, so a property that just return the value of a field will behave the same as the field example above.

Evan when the property is not inlined, it will be in the CPU cache after it has been called the first time. So ales it is very complex or the loop goes round a lot of times it take a lot longer to call the first time round the loop, maybe by more than a factor of 10.

In the old days, it used to be easy because all memory access and cpu actions took about the same time. These days the processor cache can easily change the timing for some memory accesses and method calls by over a factor of 100. Profilers tend to still assume that all memory access take the same time! So if you profile you will be told to make changes that may not have any effect in the real world.

Changing the code to:

int limit = something.awesome; 
for(int i = 0; i < limit; i++) 
{ 
// Do cool stuff 
}

Will in some cases spread it up, but also makes it more complex. However

int limit = myArray.length; 
for(int i = 0; i < limit; i++) 
{ 
   myArray[i[ = xyn;
}

is slower then

for(int i = 0; i < myArray.length; i++) 
{ 
   myArray[i[ = xyn;
}

as .net check the bound of arrays each time they are accessed and have logic to remove the check when the loop is simple enough.

So it is best to keep the code simple and clear until you can prove there is a problem. You make a lot better gain by spending your time improving the overall design of a system, this is easy to do if the code you start with is simple.

like image 22
Ian Ringrose Avatar answered Sep 29 '22 23:09

Ian Ringrose


It evaluated every time. Try this in a simple console app:

public class MyClass
{
    public int Value
    {
        get
        {                
            Console.WriteLine("Value called");
            return 3;
        }
    }
}

Used in this way:

MyClass myClass = new MyClass();
for (int i = 0; i < myClass.Value; i++)
{                
}

Will result in three lines printed to the screen.

Update

So to avoid this, you could this:

int awesome = something.awesome;
for(int i = 0; i < awesome; i++)
{
// Do cool stuff
}
like image 35
Martin Ingvar Kofoed Jensen Avatar answered Sep 29 '22 23:09

Martin Ingvar Kofoed Jensen


something.awesome will be reevaluated every time you go through the loop.

It would be better to do this:

int limit = something.awesome;
for(int i = 0; i < limit; i++)
{
// Do cool stuff
}
like image 22
Oded Avatar answered Sep 30 '22 00:09

Oded