Programming for Performance


Getting optimal performance from a .NET Micro Framework device does not make many additional demands of your programming. However, there are some aspects of your code to which you might wish to pay attention to get the maximum speed out of your programs.

Note 

Some of the techniques we describe later in this chapter are contrary to what most would regard as good programmer behavior. They can make your code harder to understand and maintain. For this reason, use them with caution. You should also make sure that you can properly determine the effects of the changes, because not all of them will improve performance in every situation. To ensure the validity of your approach, write additional test code to measure the effect of the suggested changes and ensure that they improve performance. You should also make sure that you perform such tests on actual hardware rather than on the emulator.

Improving Iteration

Programs spend much of their time working their way through data. There are a number of program constructions for doing this. The C# language provides a construction called foreach.

 foreach (FlashlightUse logItem in UseLog) {    if (logItem != null) {       Debug.Print(logItem.ToString());    } } 

The preceding code takes each FlashlightUse item out of the collection called UseLog and prints it. It uses the foreach construction, which is useful because it performs all the casting required to pull items of a particular type from a collection. However, it is very inefficient, both in the speed at which it executes and because it will create an instance of an iterator object to produce the sequence of instances. This will lead to a performance hit. It is more efficient if coded without the foreach construction, although the code produced is untidier.

 for (int i=0; i < UseLog.Count; i++) {    FlashlightUse logItem = (FlashlightUse) logItem[i];    if (logItem != null) {       Debug.Print(logItem.ToString());    } } 

This code is a little harder to understand, but it should run more quickly and result in less garbage collection. However, there are still improvements to be made. At present, the code accesses the Count property in UseLog each time it tests to see if the loop has finished.

 for (int i=0; i < UseLog.Count; i++) 

It performs this test each time the loop is executed. What is not obvious from the use of Count is that this is a property. Thus, each time the code accesses the Count value, there will be a method call into the ArrayList to determine the value. Because this value should not change during the execution of the loop, you can get further performance gains by creating a constant.

 int count = UseLog.Count; for (int i=0; i < count; i++) {    FlashlightUse logItem = (FlashlightUse) logItem[i];    if (logItem != null) {       Debug.Print(logItem.ToString());    } } 

The code can now use the value directly at any time.

Accessing Member Data

The C# language allows programmers to manage access to the data held inside instances of classes. By making the actual value of a property private and then providing public get and set methods, it is possible to ensure that the data inside an object is always valid.

 public class Sprite {    private int x;    public bool SetX(int newX)    {       if ((x < 0) || (x > 319))       {          return false;       }       x = newX;       return true;    }    public int GetX()    {       return x;    } } Sprite sp = new Sprite(); sp.SetX(100); 

The previous code uses the Sprite class to hold information about an object drawn on the display in a computer game. The property x holds a private x-coordinate value. The SetX method will allow the x-coordinate to be set only if it is in the range 0 through 319, inclusive. This is good programming practice and will ensure that the program does not position a Sprite off the display. However, this negatively affects performance because reading and writing the property in the object will take longer. You can save considerable time by making the x-coordinate public and using it directly.

 public class Sprite {    public int X; } Sprite sp = new Sprite(); sp.X=120; 

In the context of a graphics application such as this, it is often the case that the underlying draw libraries perform validation of coordinates anyway, so the use of such defensive programming techniques is superfluous. Furthermore; the consequences of a Sprite moving off a display are not the same as changes to the balance of a bank account, and the design of your program should reflect this.

Thus, during the software-writing process, you should be balancing a need for reasonable performance against the risk of damage to a particular data member. You should also determine whether the program performs validation of values at some other stage.

Member Data and Properties

The C# language provides a property mechanism that also provides access to data stored in classes.

 public class Sprite {    private int x;    public int X    {       get       {          return x;       }       set       {          if ((x >= 0) && (x < 320))          {             x = value;          }       }    } } 

The content of the get and set behaviors is very like the get and set methods; the methods are called when the property is accessed.

 Sprite sp = new Sprite(); sp.X=120; 

However, when looking at the code without checking back to the source of the class itself, the use of a property looks exactly like an access to a public member of the class, so your program may be calling large methods without being aware of it. One way you can determine the nature of access to a class like this is to use the IntelliSense of Microsoft Visual Studio 2005. This shows a different icon for a property than for a member.

Note 

This also has implications with regard to side effects of property access. Whereas changing a member of a class has no other effects on your program, executing a method in a class could. This situation, although not having a direct bearing on program efficiency, is something you should bear in mind when examining code.

With regard to performance, you can gain improvements by replacing properties by direct access to class members. This has the additional benefit of not requiring that you change the code using the property at all, because the syntax is exactly the same.

Performance and Class Hierarchies

One of the key components of object-oriented programming is the creation of class hierarchies that allow one class to make use of the behaviors of another. You can use a general parent class as the base of a more specialized child class that overrides behavior in the parent and may also provide additional functionality. However, this programmer flexibility exists at the expense of considerable effort on the part of the runtime system.

Although the .NET Micro Framework does optimize the management of virtual method calls, it is still the case that class hierarchies do incur a performance hit. As a general design principle, you should keep them as flat as possible, but you can remove them entirely by adding member data to a generic class that then modulates the behavior of the method.

As an example, consider that the Sprite class we created earlier requires two behaviors, one for a sprite under computer control and one for a sprite under human control. Object-oriented design would have us create child classes that contain overrides of methods in the parent to provide the appropriate behavior. However, a more efficient solution is to change a class by adding a property that informs the relevant methods so that they can perform appropriately.

 public bool IsHumanPlayer; public void DoControl() {    if (IsHumanPlayer)    {       // Human update here    }    else    {       // Computer update here    } } 

When the program calls the DoControl method, it will select the behavior dictated by the property. This resolves down to a simple test and will be more efficient than the management of separate class instances and overridden methods.

Note 

Such code design goes against the principles of object-oriented design, so you should it sparingly and on objects for which you plan much interaction.

Data Types and Performance

The .NET Micro Framework will usually be on devices that have no hardware floating-point abilities. This means that the device must perform floating-point manipulation using software, which is a very slow process. For optimum performance, code using integers in preference to floating point, where possible. It is rare that your program would need the range and precision of a floating-point value.

As an example of this principle in action, you need to look no further than the Microsoft. SPOT.Math class, which contains sin and cos methods that return results as scaled integers rather than as full floating-point values. You can use these in the calculation of things like display coordinates with adequate precision, but without affecting performance. In general, you should try to store data as integers, if possible. This may also save on memory.

Wait Loops and System Performance

The .NET Micro Framework provides a powerful set of resources for event management. It is important that you use these whenever possible. You should avoid writing code that repeatedly checks for an event in a process called busy waiting. Instead, you should attach a handler to the event, which will run only when the program fires the event itself. You can see examples of busy waiting in the early flashlight programs that we created in Chapter 4, in which a loop continuously reads an input pin and reflects it onto an output pin.

 while (true) {    lampOutput.Write(switchInput.Read()); } 

Such a loop will work but will have a serious impact on the rate at which any other threads are able to execute. If you really must busy wait, you should add a sleep instruction to the wait loop so that other processes are able to execute.

 while (true) {    lampOutput.Write(switchInput.Read());    System.Threading.Thread.Sleep(100); } 

This code is virtually the same, and to the user will be adequately responsive, in that the light will change state within a tenth of a second of the input pin changing state. However, it will have a much less serious impact on other threads.

Wait Loops and Power Consumption

This aspect of code design also has implications for battery consumption as well. When the .NET Micro Framework has nothing to do, that is, all user threads are executing Sleep calls and there are no events pending, it will call through into the hardware abstraction layer (HAL) to request the sleep mode of the underlying device. If the firmware supports this, it will then be able to put the processor into a low-power mode and thus reduce battery consumption. This is another strong incentive to ensure that the processor is not kept permanently busy. (In the first version of our flashlight software created in Chapter 4, it would be impossible to enter low-power mode because the processor is running all the time.)

Object Management

The .NET Micro Framework has a very powerful garbage collector, which uses specially designed algorithms to identify unused objects and reclaim the memory they use. However, time spent by the processor collecting garbage is not available to your program, so you should try to minimize the amount of garbage that your program creates-that is, avoid creating and destroying objects unnecessarily. This has an additional benefit, because the program will also spend less time constructing instances.

Ideally, your programs should create all required instances of classes when they start execution and then use them throughout the execution of the program. As an example, in the game program used earlier, rather than removing a Sprite instance destroyed during game play, it should instead have a state property that tells the program not to draw it. This will allow the program to reuse the same instance just by setting the state property to bring it back into play.

String Manipulation

String manipulation is one action that creates and destroys large numbers of objects. It is important to be aware of the fact that string manipulation can be the cause of much work.

 string message = errorText +                   " logged at " +                   DateTime.Now.TimeOfDay.ToString(); 

The preceding code creates a status message out of an error text, a string, and the date and time. When code performs the string concatenation, it will cause the creation of at least one intermediate string instance. The full .NET Framework provides a StringBuilder class that you can use to efficiently create a message out of a number of strings, but it does not exist in the .NET Micro Framework.

It may be advisable to make use of arrays of characters in preference to strings, particularly if the content changes rapidly. If you must use a string, there is a constructor for the string class that accepts a character array as a parameter.




Embedded Programming with the Microsoft .Net Micro Framework
Embedded Programming with the Microsoft .NET Micro Framework
ISBN: 0735623651
EAN: 2147483647
Year: 2007
Pages: 118

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net