Introduction

The current implementation of .NET generics is used mainly to make type-safe collections faster and more easy to use. Doing calculations on generic types is not as straightforward.

The problem

An example for doing calculations on generic types would be a generic method to calculate the sum of all elements in a List<T>. Of course, summing up all elements of a list only makes sense if T is a type such as int, double, decimal that defines an addition operation.

Somebody coming from a C++ background might implement such a method like this:

This is not possible in C# because unconstrained type parameters are assumed to be of type System.Object, which does not define a + operation.

To constrain type parameters in C#/.NET, you specify interfaces that the type has to implement. The problem is that interfaces may not contain any static methods, and operator methods are static methods.

So with the current constraint system, it is not possible to define operator constraints.

A clean way to enable numerical computations would be to let the basic data types like int, float, double, decimal etc. implement an interface for arithmetic operations. Then this interface could be used to constrain the type parameters. This would work similar to the IComparable<T> interface that all basic data types implement.

I tried to convince the people at Microsoft to implement such an interface, but apparently they won't be able to do it in time for Whidbey.

We are on our own

Many people have been thinking about this problem, among them Eric Gunnerson and even Anders Hejlsberg.

The solution proposed by Anders Hejlsberg was to have an abstract class Calculator<T> that has to be specialized for each primitive type. The generic type would then use an instance of the appropriate calculator to do the calculations.

There are many other creative solutions, but all of them have the drawback that they involve some kind of dynamic method invocation (virtual methods, interfaces or delegates). So while they make it possible to perform calculations with generic type parameters, the performance is unacceptably low for numerical applications.

The Solution

The first person to come up with a solution that does not involve any virtual method invocation was Jeroen Frijters. The solution uses the fact that constraining type parameters using interfaces is not the same as casting to interfaces. Calling a method using an interface has the overhead of dynamic method dispatch, but calling a method on a type parameter that is constrained by an interface has no such overhead.

Here is an example. It is a generic method that sorts two numbers. The type T has to implement IComparable<T> so that we can be sure that it has the CompareTo method. But nevertheless, calling the CompareTo method does not have the overhead normally associated with interfaces.

The approach suggested by Jeroen Frijters uses a second type parameter. To avoid unnecessary object creation and virtual method dispatch, it is best to use a value type for the type containing the operations. Here is a small example for this approach:

Performance

Since we made sure that our code does not contain any virtual method calls or unnecessary object creation, the generic code should be exactly as fast as non-generic code. To test this, I wrote a small benchmark. The result is encouraging. Even the current very limited JIT compiler manages to inline the Sum method, so the generic version is indeed exactly as fast as the non-generic version on my machine.

Please make sure that you run the benchmark in release mode, since in debug mode many very important optimizations such as method inlining are disabled.

Using operators with type parameters

We now can do calculations on generic types with acceptable performance. But we still can not use operators with type parameters. In the above example, we had to write sum=calculator.Add(sum,list[i]) instead of simply writing sum+=list[i].

This might not be a big deal if you want to perform a simple addition. But if you have large, complex methods that excessively use operators, it would be a tedious and error-prone process to convert them to use method calls instead.

Fortunately, it is possible to create a wrapper struct that defines operators. By using implicit conversions to and from the wrapped type, we can use this wrapper struct almost transparently.

This is how a wrapper struct for the above ICalculator interface might look like:

Since the operator methods in the wrapper struct have struct parameters, they will not be inlined, so the performance will suffer. But I am sure that Microsoft will fix this important performance issue soon.

Here is a suggestion about this topic that you can vote for. This is the only thing that is missing for lightning-fast and easy to use numerics in C#.

Conclusion

Even though the constraint syntax of .NET generics system is very limited, it is possible to work around those limits by using a second type parameter. This approach is a bit more work for the writer of the generic class library, but it is quite transparent for the user of the class library and it yields performance that is identical to a non-generic version. Just like with C++ templates, the runtime cost of the abstraction is zero.

There are some limitations, but these should go away very soon as Microsoft and the competing vendors improve the JIT compiler performance. So, it is possible to write numeric libraries that are as fast or even faster than compile-time template libraries such as the C++ STL.

About the code

The included project consists of interfaces, implementations and wrappers for all primitive types with a few nice additions. The list benchmark calculates the standard deviation of a large list both with generic and with non-generic methods. Another benchmark compares the performance of the non-generic System.Drawing.Point and System.Drawing.PointFstructs with a generic Point<T> version.

The code is under the BSD license, so feel free to use it for your own projects.

Please vote for this suggestion. It is more important than the presidential elections :-)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

Rüdiger Klaehn works as freelance developer in the space industry. He is very interested in functional programming languages and hopes that .NET will lead to a more widespread adoption in the industry.

It's been about two years now since this article was published - are there any news (I doubt) ?

It's clever; but still messy compared to the traditional c++

This is another classic example of where standards overwhelm the underlying functionality - this is propably the biggest flaw in the entire .net platform I've come to learn of; I was running into the same problem and stumbled upon your article - thanks - is cleared up a whole lot of questions.

When it comes to realtime or intense 3d mathematical development that you find in all good 3d games; this stuff is mostly the core of the engines and is relied upon heavily for performance and rendering accuracy.

But it still involves a delegate invocation. Delegate invocation is much faster in .NET 2.0 than in 1.1, but it still has an overhead comparable to a virtual method invocation. As you found out in your benchmark, this carries a large performance penalty.

The nice thing about my approach is that by using structs you avoid any virtual method invocation, so (when the notorious struct inlining problems of the JIT are solved), this method will be exactly as fast as working with primitive types directly.

Besides, I think the syntax with the two different type parameters is not that bad. It allows you to specify the storage type and the calculator type separately. This can be very useful. For example you can define a double calculator which defines the = operator differently so that two numbers that differ by less than 1e-30 are considered equal.

Max Shaposhnikov wrote:Very, very interesting OO methods, they are very useful but not in the context of article. .NET isn't for computational purposes.

Using c++ classes is much more convenient for such tasks than to play with objects.

The people I currently work for seem to disagree.

Of course at this time C++ gives you better performance than C#. But this will improve once the struct inlining issue is taken care of.

There is no theroetical reason why MSIL should be slower than natively compiled binaries. In fact runtime profiling and using processor-specific features will make MSIL faster than native C++ eventually. A native C++ program will usually be compiled for the lowest common denominator (e.g. x86-32), while a good and mature MSIL JIT will use all the goodies of your processor such as 64bit registers, SSE2 etc.

Of course this is a few years in the future. But the performance of .NET languages is OK for quite sophisticated numerical problems even now. The biggest issue is the struct inlining issue. And this will hopefully be taken care of soon, either by Microsoft or by one of the open source implementations.

the is for the efort. however you pointed out the problem :There is no theroetical reason why MSIL should be slower than natively compiled binaries.
you should start looking at the problem from bottom to top and you will see the difference

My idea was to avoid retyping the indexer, initialization over and over again for each concrete class and take care of it in the generic base class. After all you can have a matrix of almost any type (matrix of textboxes, etc.), that doesn't mean you want to implement and add operator to that type of matrix

However, the performance of DoubleMatrix class derived in this fashion (although there are not virtual methods) was very poor compared to the case where DoubleMatrix was directly derived from object and implemented its own indexer.

2. Implement the operators in the generic class for each possible type. Below is the * operator:

I know, this looks sick. But in spite of all the type checks, this thing runs much faster than (1) and almost as fast as a non-generic double matrix class I am using at this time. I would certainly want to use the first approach, but I can't figure out what I am doing wrong.

Maybe this has nothing to do with the work around, but simply that operations with floats really take more time than with doubles. You'd have to check this. I vaguely remember that some compiler/CPUs first convert the floats to doubles, do the calculation and then convert back to floats. This could be the case with .NET/IL.

Articles like yours are great time savers. It's easy to grasp the fundamentals of generics, but to go a step further and find a way to use them to their fullest needs dedication and attention to detail. I'm glad someone took upon himself to do so and more importantly, wrote a well written article about the process.