Colin Thomsen's Microsoft Blog

I'm a developer working on the code profiler that ships with Visual Studio 2010 Premium and Ultimate editions. At a previous company I worked on computer vision software for face and gaze tracking.

May, 2007

Posts
  • Colin Thomsen's Microsoft Blog

    C# for C++ Devs: Structs vs Classes

    • 0 Comments

    I'm from a C++ background and now I'm working quite a bit more with C# so I'm learning new things all the time. One thing that baffled me recently was caused by the difference between structs and classes in C#.

    In C++ the only difference between struct and class is the default member accessibility. For example, in the code below A::f() is private, whereas B::f() is public

    class A
    {
       
    void f();
    }

    struct B
    {
       
    void f();
    }

    That's the only difference. Structs can have member functions and classes can contain only data members. For C#, things are different, as I found out recently.

    In C#, structs are always passed by value, whereas classes are always passed by reference. What this means in practice is that anywhere you pass a struct to a function as a parameter or return it you are doing so by value.

    The confusing piece of code for me was equivalent to the following:

    struct Animal
    {
       
    public int Spots;
    }

    class Program
    {
       
    static void Main(string[] args)
        {
           
    List<Animal> allAnimals = new List<Animal>();
            allAnimals.Add(
    new Animal());
            allAnimals.Add(
    new Animal());

           
    foreach (Animal animal in allAnimals)
            {
                animal.Spots = 5;
           
    }
          
    Debug.WriteLine(String.Format("First animal spots: {0}", allAnimals[0].Spots));
        }
    }

    When I compiled the code above I got the error:

    error CS1654: Cannot modify members of 'animal' because it is a 'foreach iteration variable'

    How strange, I thought. OK, maybe in a foreach loop you can't modify public members. Let's try calling a function instead: 

    struct Animal
    {
        public void setSpots(int NewSpots)
       
    {
            Spots = NewSpots;
        }
       
    public int Spots;
    }

    class Program
    {
       
    static void Main(string[] args)
        {
           
    List<Animal> allAnimals = new List<Animal>();
            allAnimals.Add(
    new Animal());
            allAnimals.Add(
    new Animal());

           
    foreach (Animal animal in allAnimals)
            {
                animal.setSpots(5);
           
    }
          
    Debug.WriteLine(String.Format("First animal spots: {0}", allAnimals[0].Spots));
        }
    }

    So the compile error went away, but the message printed out was:

    First animal spots: 0

    I was expecting 5 here. After reading a little bit about structs and classes in C#, the penny dropped. Each iteration through allAnimals was getting a copy of the animal and calling setSpots. If I changed the definition of Animal to a class instead of struct, I could use the original code.

    class Animal
    {
       
    public int Spots;
    }

    class Program
    {
       
    static void Main(string[] args)
        {
           
    List<Animal> allAnimals = new List<Animal>();
            allAnimals.Add(
    new Animal());
            allAnimals.Add(
    new Animal());

           
    foreach (Animal animal in allAnimals)
            {
                animal.Spots = 5;
           
    }
          
    Debug.WriteLine(String.Format("First animal spots: {0}", allAnimals[0].Spots));
        }
    }

    Incidentally, members of structs also do not have default public accessibility in C#.

  • Colin Thomsen's Microsoft Blog

    Basic Profiler Scenarios

    • 1 Comments

    This post was going to cover some basic scenarios discussing the differences between sampling and instrumentation and when you would choose to switch methods, but then I found there is already something like that in MSDN. If you haven't already, go and take a look. See if you can improve the performance of the PeopleTrax app.

    Instead I'll discuss sampling and instrumentation from a user's perspective. There are already many definitions of sampling vs instrumentation so I won't repeat them.

    For some background reading on the sampling aspect, take a look at David Gray's post. There are a few things that he hasn't covered in that post. The main question I had was should I use sampling or instrumentation?

    A generic answer to that would be:

    • If you know your performance problem is CPU-related (i.e. you see the CPU is running at or near 100% in task manager) then you should probably start with sampling.
    • If you suspect your problem may be related to resource contention (e.g. locks, network, disk etc), instrumentation would be a better starting point.

    Sometimes you may not be sure what type of performance issue you are facing or you may be trying to resolve several types of issues. Read on for more details.

    Sampling

    Why use sampling instead of instrumentation?

    Sampling is lighter weight than instrumentation (see below for reasons why instrumentation is more resouce intensive) and you don't need to change your executable/binaries to use sampling.

    What events do you sample with?

    By default the profiler samples with clock cycles. This should be familiar to most users because they relate to the commonly quoted frequency of the machine. For example, 1 GHz is 1 billion clock cycles / second. If you use the default profiler setting for clock cycles that would mean 100 samples every second on a 1 GHz machine.

    Alternatively, you could choose to sample using Page Faults, which might occur frequently if you are allocating/deallocating memory a lot. You could also choose to profile using system calls or some lower level counter.

    How many samples is enough to accurately represent my program profile?

    This is not a simple question to answer. By default we only sample every 10000000 clock cycles, which might seem like a long time between samples. In that time, your problematic code might block waiting on a lock or some other construct and the thread it is running in might be pre-empted allowing another thread to run. When the next sample is taken the other thread could still be running which means the problematic code is not included in the sample.

    The risk of missing the key data is something that is inherent in any sample-based data collection. In statistics the approach is to minimize the risk of missing key information by making the number of samples large enough relative to the general population. For example, if you have a demographic that includes 10000 people, taking only 1 sample is unlikely to be representative. Taking a sample of 1000 people might be considered representative. There are more links about this on Wikipedia.

    Won't this slow down my app?

    No, not really. When a sample is taken the current thread is suspended (other application threads continue to run) so that the current call stack can be collected. When the stack walk is finished, execution returns to the application thread. Sampling should have a limited effect on most applications.

    Sounds good, why use instrumentation?

    See below.

    Instrumentation

    Why use instrumentation?

    As discussed above, sampling doesn't always give you the whole picture. If you really want to know what is going on with a program the most complete way is to keep track of every single call to every function.

    How does instrumentation work (briefly)?

    Unlike sampling, with instrumentation the profiler changes the binary by inserting special pieces of code called probes at the start and end of each function. This process is called 'instrumenting the binary' and it works by taking a binary (dll or exe) along with its PDB and making a new 'instrumented binary'. By comparing a counter at the end of the function with the start, it is easy to determine how long a function took to execute.

    What if I call other people's code?

    Usually you don't have access to the PDB files for other people's code which means you can't instrument it. Fortunately as part of the instrumentation process the profiler inserts special probes around each call to an external function so that you can track these calls (although not any functions that they might call).

    Why not just use Instrumentation all the time?

    Computers execute a lot of instructions in 10000000 clock cycles, so using instrumentation can generate a LOT of data compared with sampling. The process of calling the probe functions in an application thread can also degrade performance more than sampling would.

  • Colin Thomsen's Microsoft Blog

    Learning to Profile

    • 0 Comments

    I went to a meeting with Rico the other day and he showed us a few approaches he uses when solving performance issues. He is a performance engineer with many years of experience so it really was a case of watch and learn. This got me thinking about how people can best learn to use performance tools.

    One starting point in this process is to consider my own experience learning a more mature dynamic code analysis tool - the debugger. Think back to the first time you ever started up a program running under a debugger. What was the first thing you did? My first debugging experience went something like this:

    • Set a breakpoint at the beginning of main() - this was C/C++ afterall.
    • Run the code in the debugger. Hey, it stopped. cool.
    • Step through a few lines of code and inspect the values of some local variables.

    Sit back and think that's pretty cool - maybe I'll have to use a few less printfs to work out what's going on with my program. That's pretty much it. Gradually I learnt more and more about things like:

    • The difference between Step In, Step Over, Step Out, Run to Cursor
    • The value of different types of breakpoints like conditional breakpoints, data breakpoints etc.
    • The value of the Watch window. I'm still surprised by how much you customize the output to make it easier to find issues.
    • The various other windows - threads, memory, etc. etc.
    • Etc.

    It took a long to discover some of these features. It took even longer to use them almost automatically while debugging.

    Obviously the learning curve depends a lot upon the tool you use. Visual Studio tries to be more intuitive and easy to use than something like WinDbg, which is a command-line tool. Even with the ease of use of the visual debugger, you still need to know the typical debugging pattern (using breakpoints) before you can use the tool effectively.

    Fewer people have used code profilers than debuggers and the tools are still less mature than their debugger equivalents, so it is harder for new programmers to profile their code than to debug it. In an ideal world we might have a 'fix my code' button or at the very least a 'highlight problem code lines' feature, but for now we need to develop patterns that developers can use to do this themselves.

    What features would make profiling easier for you? Are we missing a fundamental concept (the equivalent of 'set breakpoint' in debugging land) that would make things so much easier?

Page 1 of 1 (3 items)