Exploring the .NET Core Part 6: Use IEquatable for Higher-Performance Equals()

November 24, 2014 Leave a comment

This is part 6 of my Exploring the .NET Core Series.

Let’s say you are writing a custom IList<T> which contains the following code:

public class MyList<T> : IList<T>
{
    private T[] array;

    ...

    public int IndexOf(T item)
    {
        for (int i = 0; i != array.Length; ++i) {
            if (this.array[i].Equals(item)) {
                return i;
            }
        }

        return -1;
    }
}

The above code uses T‘s implementation of Object.Equals(), which is defined as:

public virtual bool Equals(Object obj);

If T is a value type, it will be automatically boxed by the compiler, which has a slight performance cost. However, if you knew that T implemented IEquatable<T>, then you could avoid the boxing entirely. For example, this code would be slightly better performing than the above for value types:

public class MyList<T> : IList<T> where T : IEquatable<T>
{
    // Everything else is the same as above
}

However, it is inadvisable to require MyList to only contain objects which implement IEquatable<T>.

You can get the best of both worlds by using EqualityComparer<T>.Default to choose IEquatable<T>.Equals() when available, and Object.Equals() otherwise, as follows:

public class MyList<T> : IList<T>
{
    private T[] array;

    ...

    public int IndexOf(T item)
    {
        return IndexOf(item, EqualityComparer<T>.Default);
    }

    public int IndexOf(T item, IEqualityComparer<T> equalityComparer)
    {
        for (int i = 0; i != array.Length; ++i) {
            if (equalityComparer.Equals(this.array[i], item)) {
                return i;
            }
        }

        return -1;
    }
}

For the same reason, it is a good idea to implement IEquatable<T> on objects for which you expect .Equals() to be called a lot, such as those you put into arrays, lists, or hash tables.

Exploring the .NET Core Part 5: Keep Indexers Trivial to Allow JIT Optimization

November 21, 2014 Leave a comment

This is part 5 of my Exploring the .NET Core Series.

Keep the implementation of an indexer as trivial as possible to allow the JIT optimization of removing array bounds checking to work. For example, don’t check if a member variable is null; just use it and allow the NullReferenceException to happen naturally.

In other words, use:

public T this[int index]
{
    get
    {
        return this.array[index];
    }
}

not:

public T this[int index]
{
    get
    {
        if (this.array == null)
            throw new NullReferenceException();
        return this.array[index];
    }
}

Exploring the .NET Core Part 4: The Requires Convenience Class

November 20, 2014 Leave a comment

This is part 4 of my Exploring the .NET Core Series.

The .NET Core includes a convenience class called Requires, which looks like:

internal static class Requires
{
    [DebuggerStepThrough]
    public static T NotNull([ValidatedNotNull]T value, string parameterName)
        where T : class // ensures value-types aren't passed to a null checking method
    {
        if (value == null)
        {
            throw new ArgumentNullException(parameterName);
        }

        return value;
    }

    ...
}

This allows other methods to write code like:

public void Foo(string bar)
{
    Requires.NotNull(bar);

    ...
}

Exploring the .NET Core Part 3: Making Methods Debugger-Friendly

November 19, 2014 Leave a comment

This is part 3 of my Exploring the .NET Core Series.

This is about how to use attributes to make your code more debugger-friendly. There are a few key attributes:

DebuggerStepThrough

Occasionally a method is so simple that it doesn’t make sense to have the debugger step into it. The System.Diagnostics.DebuggerStepThroughAttribute instructs the debugger to step through the code instead of stepping into the code.

Here is a sample usage from the .NET core:

internal static class Requires
{
    [DebuggerStepThrough]
    public static void Range(bool condition, string parameterName, string message = null)
    {
        if (!condition)
        {
            FailRange(parameterName, message);
        }
    }
}

DebuggerBrowsable

The System.Diagnostics.DebuggerBrowsableAttribute determines if and how a member is displayed in the debugger variable windows.

Here are some sample uses from the .NET Core:

public partial struct ImmutableArray<T> : IReadOnlyList<T>, IList<T>, IEquatable<ImmutableArray<T>>, IImmutableList<T>, IList, IImmutableArray, IStructuralComparable, IStructuralEquatable
{
    ...

    [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
    internal T[] array;

    ...

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    bool ICollection<T>.IsReadOnly
    {
        get { return true; }
    }

DebuggerDisplay

The System.Diagnostics.DebuggerDisplayAttribute controls how a class or field is displayed in the debugger variable windows. For example, if you were writing a class to represent a complex number, you could use the DebuggerDisplay attribute to render the complex number as (a, b) or even a + bi.

Here are some sample uses from the .NET Core:

[DebuggerDisplay("Count = {stack != null ? stack.Count : 0}")]
internal static class AllocFreeConcurrentStack<T>
{
    ...
}
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public partial struct ImmutableArray<T> : IReadOnlyList<T>, IList<T>, IEquatable<ImmutableArray<T>>, IImmutableList<T>, IList, IImmutableArray, IStructuralComparable, IStructuralEquatable
{
    ...
}

DebuggerTypeProxy

The System.Diagnostics.DebuggerTypeProxyAttribute allows the developer to specify a display proxy for a type, allowing the developer to completely tailor the view for the type.

Here is a sample use from the .NET Core:

public partial struct ImmutableArray<T>
{
    [DebuggerTypeProxy(typeof(ImmutableArray<>.Builder.DebuggerProxy))]
    public sealed class Builder : IList<T>, IReadOnlyList<T>
    {
        ....

Exploring the .NET Core Part 2: Cache ThreadLocal Variables in Locals

November 18, 2014 Leave a comment

This is part 2 of my Exploring the .NET Core Series.

Developers should cache thread-local variables, that is, variables marked with the System.ThreadStaticAttribute, in local variables in a method to avoid unnecessary TLS hits on repeated access.

Here’s some sample code which implements this:

[ThreadStatic]
private static Stack<RefAsValueType<T>> stack;

public static void TryAdd(T item)
{
    Stack<RefAsValueType<T>> localStack = stack; // cache in a local to avoid unnecessary TLS hits on repeated accesses
    if (localStack == null)
    {
        stack = localStack = new Stack<RefAsValueType<T>>(MaxSize);
    }

    // Just in case we're in a scenario where an object is continually requested on one thread
    // and returned on another, avoid unbounded growth of the stack.
    if (localStack.Count < MaxSize)
    {
        localStack.Push(new RefAsValueType<T>(item));
    }
}

Exploring the .NET Core Part 1: Annotate Pure Methods With PureAttribute

November 17, 2014 Leave a comment

This is part 1 of my Exploring the .NET Core Series.

Developers should annotate pure methods, that is, methods that do not make any visible state changes, with System.Diagnostics.Contracts.PureAttribute.

John Carmack, in his article In-Depth: Functional Programming in C++, notes many advantages of pure functions:

Pure functions have a lot of nice properties.

Thread safety. A pure function with value parameters is completely thread safe. With reference or pointer parameters, even if they are const, you do need to be aware of the danger that another thread doing non-pure operations might mutate or free the data, but it is still one of the most powerful tools for writing safe multithreaded code.

You can trivially switch them out for parallel implementations, or run multiple implementations to compare the results. This makes it much safer to experiment and evolve.

Reusability. It is much easier to transplant a pure function to a new environment. You still need to deal with type definitions and any called pure functions, but there is no snowball effect. How many times have you known there was some code that does what you need in another system, but extricating it from all of its environmental assumptions was more work than just writing it over?

Testability. A pure function has referential transparency, which means that it will always give the same result for a set of parameters no matter when it is called, which makes it much easier to exercise than something interwoven with other systems. I have never been very responsible about writing test code; a lot of code interacts with enough systems that it can require elaborate harnesses to exercise, and I could often convince myself (probably incorrectly) that it wasn’t worth the effort.

Pure functions are trivial to test; the tests look like something right out of a textbook, where you build some inputs and look at the output. Whenever I come across a finicky looking bit of code now, I split it out into a separate pure function and write tests for it. Frighteningly, I often find something wrong in these cases, which means I’m probably not casting a wide enough net.

Understandability and maintainability. The bounding of both input and output makes pure functions easier to re-learn when needed, and there are less places for undocumented requirements regarding external state to hide.

Another advantage of pure methods, as noted by this LWN article, is that the compiler can apply higher optimization than normal to the method. Two such optimizations that may be applied are common subexpression elimination and dead code elimination.

Here’s some sample code which uses PureAttribute:

[Pure]
public static ImmutableArray<T> Create<T>()
{
    return ImmutableArray<T>.Empty;
}

The Power of Central Banks

May 14, 2014 Leave a comment

In Europe, the transformation in the sovereign debt market since Mario Draghi’s “whatever it takes” promise in July 2012 has been remarkable.  Ireland, which just exited an IMF bailout program in December, can now issue 5-year debt at yields well below 5-year Treasuries.

PIMCO Secular Outlook, May 2014 – The New Neutral

Categories: Finance / Investing Tags:
Follow

Get every new post delivered to your Inbox.