Blog

Written by:  Kathy Nguyen  7/19/2010 4:15 PM 

Parallel Computing

It is not unusual for workstations and PCs to operate on multiple threads at the same time through the use of either two or four CPUs (also known as cores). Experts anticipate seeing many more cores in the near future. By writing parallel code, developers can make the most of existing hardware, distributing workloads over more than one processor. This time of parallelization used to mean manipulating multiple locks and threads, but that is no longer always the case. Parallel programming is now facilitated by the .NET Framework 4 and Visual Studio 2010, which provide new diagnostic tools, class library types, and runtime. These features make the process of parallel development much simpler, because developers can work in a natural language, avoiding direct creation and manipulation of the thread pool and individual threads. The result is scalable, fine-grained, and efficient code, as illustrated below.

Tasks Parallel

What is data parallelism? The term refers to operations that occur simultaneously (e.g., in parallel) while operating on components of a single data collection or data array. Within the Parallel class (System.Threading.Tasks namespace), overloads using the ForEach and For methods can support imperative syntax that uses data parallelism. Data parallelism is also supported with the Parallel class by the Task Parallel Library (TPL), which relies on method-based implementations of the For and ForEach loops in Visual Basic. To keep track of parallel data, the information is divided into smaller parts, thereby allowing different sections to be operated upon at the same time by using more than one thread.

When writing loop logic, approach it in the same way as you would approach a sequential loop. When writing the logic for a Parallel.ForEach or a Parallel.For loop, you don’t need to queue items or build individual threads, nor do you need to take locks for basic loops. TPL can do all the basic work on your behalf, as shown in this example (a basic foreach loop and its parallel counterpart):

Simple example:

//Sequential
long sum = 0;
foreach (var number in numbers)
{
    sum += number;
}
//Parallel
long sum = 0;
int[] numbers = Enumerable.Range(0, 20).ToArray();
Parallel.ForEach(numbers, number => 
{
    Interlocked.Add(ref sum, number);         
});

Example with break:

//Sequential
long sum = 0;
foreach (var number in numbers)
{
    sum += number;
    if(number==10)
    break;
}
//Parallel
long sum = 0;
int[] numbers = Enumerable.Range(0, 20).ToArray();
Parallel.ForEach(numbers, (number, loopContext) => 
{
    Interlocked.Add(ref sum, number); 
    if(number==10)
    loopContext.Break(); 
});

PLINQ

PLINQ, also known as Parallel LINQ, works in parallel with LINQ to Objects. With PINQ, all the LINQ operators for standard queries are implemented as extensions of the System.Linq namespace. There are also some extra operators to support the parallelism. PLINQ is advantageous because it offers the power of parallelism, yet retains the easy readability and ease of LINQ syntax. Much like the TPL described above, PLINQ operations are based on what the host computer is capable of when querying the degree of concurrency.

New Advances in Programming Languages

C# and Visual Basic have several new features in their core language. These include optional and named parameters, dynamic dispatch, implicit line continuations, statement lambdas, and more. Developers are well advised to keep abreast of these advances.

Dynamic Support

A new “dynamic” type has been introduced for late-binding support in C # 4.0. With this introduction, developers can enjoy easier access to (1) the HTML DOM (document object model), (2) IronPython libraries and other dynamic APIs, and (3) Office automation APIs and other COM APIs. This opens up many new possibilities.

Example:

dynamic processes = System.Diagnostics.Process.GetProcesses();
// The property Length is valid, and will not cause a runtime exception
var length = processes.Length; 
// Will not cause a compiler error, but will cause a runtime exception.
processes.NonExistingMethod()
// var length is in this case a dynamic type. 
//The assignment will not cause a compiler error.
length = "a string ";

New Argument Introductions – Named and Optional Arguments

Optional and named arguments are among the new introductions in the 2010 version of Visual C#. With optional arguments, developers can omit arguments that were previously required for some parameters. The new named arguments let developers associate arguments with specific parameter names rather than their position in the parameter list. With a little practice, both optional and named argument techniques can be used for delegates, constructors, indexers, and methods. The key thing to remember is that the parameter list is no longer of paramount importance, because the order of evaluation now depends on the order in which items appear in the argument list and not the order of the parameter list.

When using optional and named parameters in conjunction with one another, developers can now supply only a few listed parameters when supplying arguments. This helps Microsoft Office automation APIs and other calls to COM interfaces.

Example of named arguments:

// Note that with named parameters the order does not matter.
var price = CalculateCost(quanity: 123, unitPrice: 64);
private decimal CalculateCost(decimal unitPrice, int quantity)
{
    return unitPrice * quantity;
}

Example of optional arguments

// don’t have to pass in quantity since it is optional
CalculateCost(10.2);
// don’t have to pass in quantity since it is optional
CalculateCost(unitPrice: 10.2); 
private decimal CalculateCost(decimal unitPrice, int quantity = 1)
{
    return unitPrice * quantity;
}

Generic Parameters for Contravariance and Covariance

For maximum flexibility in assigning and implementing generic types, use contravariant and covariant generic type parameters. The latter give your assignments the appearance of ordinary polymorphism. As an illustration, let’s imagine that you have a Derived class and a Base class. With polymorphism, you can treat the Base class as your variable and assign the Derived class to it (for example by assigning IEnumerable to an Enumerable variable).

Example of covariance:

public abstract class Animal
{
    public abstract void Sing();
}
public class Cat : Animal
{
    public override void Sing()
    {
        Console.Write("meow meeeow ");
    }
}
class Program
{
    public static void MakeTheAnimalsSing(IEnumerable<Animal> animals)
    {
        foreach (Animal animal in animals)
        {
            animal.Sing();
        }
    }
    static void Main(string[] args)
    {
        ICollection<Cat> cats = new List<Cat>();
        cats.Add(new Cat());
        // This was not allowed in C# 3.0, but in C# 4.0 allows this due to
        //covariance with genetics and IEnumerable.
        MakeTheAnimalsSing(cats); 
        // Also an example of covariance; IEnumerable<Cat> 
        //is assigned to IEnumerable<Animal>.
        IEnumerable<Animal> animals = cats; 
    }
}
Another simple example of covariance with LINQ:
IEnumerable<Person> people = from Employee e in db.Employees 
where e.Salery <= 40000 
select e;
Contravariance:
public class AnimalComparer : IComparer<Animal>
{
    public int Compare(Animal x, Animal y)
    {
        return x.GetHashCode().CompareTo(y.GetHashCode());
    }
} 
var catA = new Cat();
var catB = new Cat();
// Example of contravariance. AnimalComparer implements IComparer<Animal>.
// IComparer<Animal> is assigned to IComparer<Cat>.
IComparer <Cat> comparer = new AnimalComparer(); 
var result = comparer.Compare(catA, catB);

New Runtime Environment for Dynamic Language

There is now a new runtime (dynamic language runtime; DLR) environment that adds services to the CLR for dynamic languages. This facilitates both the development and implementation of dynamic features in static languages and dynamic languages on the .NET framework. IronRuby, IronPython, and other dynamic languages now enjoy full support from the runtime.

F#

The F# language provides support for procedural (imperative) programming, traditional object-oriented programming, and functional programming. The Visual F# product provides support for adding F# code to exiting (non F#) applications or to develop full F# application. As part of the .NET Framework language family, F# is quite similar to other functional languages in the ML family, and is a great contribution to the .NET framework.

Contracts for Code

When you need to specify object invariants, preconditions, and postconditions, turn to code contracts. What does this mean?

  • Object invariants – provide a description of the good-state of a class in its expected state.
  • Preconditions – Establish the conditions that must exist for a given property or method.
  • Postconditions – Describe what is expected at the moment when the property or method is completed.

The code contracts include a runtime analyzer, a static analyzer (suited for compiled time) and ways to mark up code with Code contract classes. To learn more about the available classes, refer to the System.Diagnostics.Contracts namespace.

Here are the primary advantages of using code contracts:

  • The document generator adds specific contract data to your existing files for XML documentation. If desired, you can add contract sections to the resulting pages through the use of Sandcastle-compatible style sheets.
  • With code contracts, you can avoid running useless test arguments that don’t meet the preconditions, thereby focusing on pithier, unit-based tests.
  • You can use the static checker to identify whether a program has contract violations, even without running that program. The checker looks for explicit and implicit contracts, including array bounds and null references.
  • Code contracts facilitate better testing, including the generation of documents, runtime checks, and the static verification of contracts.

A good thing about code contracts is that you don’t need to build a compiler or parser to begin using them for any language. They are compatible with any and all programming languages in the .NET Framework family, and you can set the level of analysis through the use of an add-in in Visual Studio. It also has the added advantage of letting you use the tool’s IntelliSense. The code contract analyzers can evaluate the name resolution and type checking, ensuring that contracts are well founded. They can also compile the contracts in the MSIL (Microsoft intermodal language) format.

Using Contract.Requires in place of Debug.Assert

Debug.Assert is excellent for stating which invariants need to be satisfied by your code at various points in the program, but it is inappropriate for specifying method-boundary contracts for three main reasons: inheritance, the visibility of preconditions, and postconditions.

First, the preconditions for overridden methods should be the same as those of their predecessor methods, and their postconditions must be at least as strong. When a programmer uses Debug.Assert, the need to manually duplicate the preconditions and postconditions increases the likelihood of introducing error. In addition, Debug.Assert lacks a means to require consistency among the overridden method contracts. In contrast, Contract.Ensures and Contract.Requires incorporate tools that ensure appropriate contract inheritance and consistency.

Second, contracts make preconditions more visible, which is necessary so that the caller can determine whether the required conditions have been satisfied before calling a method. If preconditions refer to an inaccessible internal state, the caller cannot make that determination. While Debug.Assert can specify internal consistency, Contract.Requires is superior because it allows tools and programmers to identify method contracts as such, which goes a step beyond checks for internal consistency.

Finally, we know that postconditions need to be checked upon every normal return, but with Debug.Assert this is time-consuming, laborious, and susceptible to errors because the programmer must insert it manually where needed. This is not only difficult to maintain, but also a source of unnecessary duplication. In contrast, Contract.Ensures can specify the precondition one single time at the beginning of a method and then tools interpret it where needed. Another advantage of Contract.Ensures is that it differentiates internal consistency checks and postconditions.

Contract Failure in Runtime

There are many configuration options related to failure behavior. Specifically, with the binary rewriter, users can establish which methods should be called for each contract failure. If these settings are not defined, then by default the system checks for handler registration as described in the user guide (section 7.2). The Contract.Failure method is called if and when there are no handler registrations, or if none of the registered handlers indicate that the failure has been dealt with.

How all his actually works is dependent on whether the CLR v4.0 is in use. If you are using the CLR v4.0, Environment.FailFast asserts and terminates the process. If you are not using the CLR v4.0, most likely because you are instead using .NET 2.0 or 3.5, then the assembly Micorsoft.Contracts.dll implements the contract class and Debug.Assert comes into play, called with a string that indicates whether the failure was tied to a precondition, postcondition, or something else. This frees the programmer to use customized settings and assert listeners to achieve the desired runtime behavior, such as throwing an exception, logging the failure, ignoring the error, and more.

Specifying Contracts: Using Method Calls Instead of Attributes

Some people would ask “why not use customized attributes since they don’t have any impact on the code?” However, method calls offer many benefits that outweigh those of the attributes. Specifically, let’s consider IDE support, parsing, and runtime support.

When contracts are authorized as codes, refactoring, type checking, and Intellisense are all available tools. When expressed as strings, there is no such support.

Customized attributes are compatible with a limited number of values, so consequently conditions are encoded as strings. The result of this is that a new language must be created for compatibility with all source languages, and this involves parsing strings. Of course, this functionality is already inherent in the compiler, so there is no need to duplicate the effort.

If a binary rewriter is not used, contracts based on attributes cannot be enforced at runtime. Consequently, if any preconditions need to be present at runtime, a binary rewriter must be used, or contracts must be duplicated in the code. For runtime-checked validation and a declarative contract, use Contract.RequiresAlways.

Validating Preconditions

Many programmers are already using “if (…) throw…” code to validate preconditions, but fortunately these don’t have to be converted in order to take advantage of the Code Contracts tools and use Contract.Requires. Programmers can force tools to recognize the existing code as a precondition by keeping the following factors in mind:

  • The code must precede any other codes at the beginning of the corresponding method.
  • The code may not include an else branch.
  • The code must be free of side effects.
  • The code must precede a call to Contract.Ensures/Contract.Requires/Control.EndContractBlock

The if-preconditions will be recognized automatically if Contract.Ensures/ Contract.Requires are added after the "if (...) throw ..." code. If your "if (...) throw ..." code isn’t followed by other method contracts, simply add the end marker, Contract.EndContractBlock(), as shown below.

void MyMethod(Foo foo)
{
    if (foo == null) throw new ArgumentNullException(...);
    Contract.EndContractBlock();
    ... normal method code ...
}

Complex Numbers

A new structure known as Complex can be used to represent any complex numbers. It supports all trigonometric and arithmetic operations.

BigInteger

Any language in the .NET Framework can accommodate the BigInteger structure. This new feature is a data type for arbitrary-precision integers. It supports bit manipulation and all other standard operations conducted on integers.

Search

Archive
<<February 2012>>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

Monthly