Monday, March 11, 2013

C# - The C# Memory Model in Theory and Practice, Part 2

C# - The C# Memory Model in Theory and Practice, Part 2:

'via Blog this'

"...When writing CPU-intensive code, it sometimes makes sense to use volatile fields, as long as you only rely on the ECMA C# specification guarantees and not on architecture-specific implementation details..."

C# - The C# Memory Model in Theory and Practice

C# - The C# Memory Model in Theory and Practice:

'via Blog this'

Friday, March 8, 2013

Fluent Dataflow

Fluent Dataflow:

'via Blog this'

Here's how fluent extension methods might be defined:
public static class Extensions
{
    public static TransformBlock AddTransform (
        this ISourceBlock source,
        Func transform,
        ExecutionDataflowBlockOptions options = null)
    {
        var transformBlock = new TransformBlock (transform, options ?? new ExecutionDataflowBlockOptions());
        source.LinkTo (transformBlock);
        source.Completion.ContinueWith (_ => transformBlock.Complete());
        return transformBlock;
    }
   
    public static TransformBlock AddTransform (
        this ISourceBlock source,
        Func transform,
        int maxParallelism,
        int boundedCapacity = -1,
        CancellationToken cancelToken = default (CancellationToken),
        TaskScheduler scheduler = null)
    {
        return AddTransform (source, transform, GetExecutionOptions (maxParallelism, boundedCapacity, cancelToken, scheduler));
    }
   
    public static TransformBlock AddTransform (
        this ISourceBlock source,
        Func> transform,
        ExecutionDataflowBlockOptions options = null)
    {
        var transformBlock = new TransformBlock (transform, options ?? new ExecutionDataflowBlockOptions());
        source.LinkTo (transformBlock);
        source.Completion.ContinueWith (_ => transformBlock.Complete());
        return transformBlock;
    }
   
    public static TransformBlock AddTransform (
        this ISourceBlock source,
        Func> transform,
        int maxParallelism,
        int boundedCapacity = -1,
        CancellationToken cancelToken = default (CancellationToken),
        TaskScheduler scheduler = null)
    {
        return AddTransform (source, transform, GetExecutionOptions (maxParallelism, boundedCapacity, cancelToken, scheduler));
    }
   
    public static ActionBlock AddAction (
        this ISourceBlock source,
        Action action,
        ExecutionDataflowBlockOptions options = null)
    {
        var actionBlock = new ActionBlock(action, options ?? new ExecutionDataflowBlockOptions());
        source.LinkTo (actionBlock);
        source.Completion.ContinueWith (_ => actionBlock.Complete());
        return actionBlock;
    }
   
    public static ActionBlock AddAction (
        this ISourceBlock source,
        Action action,
        int maxParallelism,
        int boundedCapacity = -1,
        CancellationToken cancelToken = default (CancellationToken),
        TaskScheduler scheduler = null)
    {
        return AddAction (source, action, GetExecutionOptions (maxParallelism, boundedCapacity, cancelToken, scheduler));
    }
   
    public static BufferBlock AddBuffer (this ISourceBlock source, DataflowBlockOptions options = null)
    {
        var bufferBlock = new BufferBlock (options ?? new ExecutionDataflowBlockOptions());
        source.LinkTo (bufferBlock);
        source.Completion.ContinueWith (_ => bufferBlock.Complete());
        return bufferBlock;
    }
   
    public static BufferBlock AddBuffer (this ISourceBlock source, int boundedCapacity = -1)
    {
        return AddBuffer (source, new System.Threading.Tasks.Dataflow.DataflowBlockOptions { BoundedCapacity = boundedCapacity });
    }
   
    public static ExecutionDataflowBlockOptions GetExecutionOptions (
        int maxParallelism = 1,
        int boundedCapacity = -1,
        CancellationToken cancelToken = default (CancellationToken),
        TaskScheduler scheduler = null)
    {   
        var options = new ExecutionDataflowBlockOptions
        {
            BoundedCapacity = boundedCapacity,
            MaxDegreeOfParallelism = maxParallelism,
            CancellationToken = cancelToken
        };
        if (scheduler != null) options.TaskScheduler = scheduler;
        return options;
    }
}

Allow an Action to be executed only nn number of times.

"Barrier" is not quite the correct word.. But this does fit my needs.
        public static Action ActionBarrier( this Action action, long remainingCallsAllowed = 1 ) {
            var context = new ContextCallOnlyXTimes( remainingCallsAllowed );
            return () => {
                if ( Interlocked.Decrement( ref context.CallsAllowed ) >= 0 ) {
                    action();
                }
            };
        }

        public class ContextCallOnlyXTimes {
            public ContextCallOnlyXTimes( long times ) {
                if ( times <= 0 ) { times = 0; }
                this.CallsAllowed = times;
            }
            public long CallsAllowed;
        }

Example:

        private static void ActionBarrierExample() {

            var foo = new Action( Foo );
            var fooWithBarrier = foo.ActionBarrier( remainingCallsAllowed: 1 );
            fooWithBarrier();
            fooWithBarrier();
            fooWithBarrier();

            var barWithBarrier = ThreadingExtensions.ActionBarrier( action: Bar, remainingCallsAllowed: 2 );
            var bob1 = new Thread( () => barWithBarrier() );
            var bob2 = new Thread( () => barWithBarrier() );
            var bob3 = new Thread( () => barWithBarrier() );
            var bob4 = new Thread( () => barWithBarrier() );

            bob1.Start();
            bob2.Start();
            bob3.Start();
            bob4.Start();

            bob1.Join();
            bob2.Join();
            bob3.Join();
            bob4.Join();

            Console.WriteLine( "enter return" );
            Console.ReadLine();
        }

C# in Depth: Implementing the Singleton Pattern

C# in Depth: Implementing the Singleton Pattern:

Table of contents (for linking purposes...)

Sixth version - using .NET 4's Lazy type

If you're using .NET 4 (or higher), you can use the System.Lazy type to make the laziness really simple. All you need to do is pass a delegate to the constructor which calls the Singleton constructor - which is done most easily with a lambda expression.
public sealed class Singleton
{
    private static readonly Lazy lazy =
        new Lazy(() => new Singleton());
   
    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
}
It's simple and performs well. It also allows you to check whether or not the instance has been created yet with the IsValueCreated property, if you need that.

SQL Server Misconceptions

SQL Server Misconceptions:

'via Blog this'

Thursday, March 7, 2013

Survey results: How cluster key size can lead to GBs of wasted space - Paul S. Randal

Survey results: How cluster key size can lead to GBs of wasted space - Paul S. Randal:

'via Blog this'


  • If you can save tens or hundreds of GBs by changing the cluster key to something much smaller, that translates directly into a reduction in size of your backups and data file disk space requirements.
  • Smaller databases mean faster backups and restores.
  • Making the nonclustered indexes smaller means that index maintenance (from inserts/updates/deletes) and index fragmentation removal will be much faster and generate less transaction log.
  • Making the nonclustered indexes smaller means that consistency checking will be much faster – nonclustered index checking takes 30% of the CPU usage of DBCC CHECKDB.
  • Reducing the width of nonclustered index records means the density of records (number of records per nonclustered index page) increases dramatically, leading to faster index processing, more efficient buffer pool (i.e. memory) usage, and fewer I/Os as more of the indexes can fit in memory.
  • Anything you can do to reduce the amount of transaction log directly affects the performance of log backups, replication, database mirroring, and log shipping.

SQL Server tempdb Data Files

SQL Server tempdb Data Files:

'via Blog this'

Why would round-robin allocation cause things to slow down for memory-spills to tempdb with a large number of files? A couple of possibilities:
  • Round-robin allocation is per filegroup, and you can only have one filegroup in tempdb. With 16, 32, or more files in tempdb, and very large allocations happening from just a few threads, the extra synchronization and work necessary to do the round-robin allocation (looking at the allocation weightings for each file and deciding whether to allocate or decrement the weighting, plus quite frequently recalculating the weightings for all files – every 8192 allocations) starts to add up and become noticeable. It’s very different from lots of threads doing lots of small allocations. It’s also very different from allocating from a single-file filegroup – which is optimized (obviously) to not do round-robin.
  • Your tempdb data files are not the same size and so the auto-grow is only growing a single file (the algorithm is unfortunately broken), leading to skewed usage and an I/O hotspot.
  • Having too many files can lead to essentially random IO patterns when the buffer pool needs to free up space through the lazywriter (tempdb checkpoints don’t flush data pages) for systems with not very large buffer pools but *lots* of tempdb data. If the I/O subsystem can’t handle the load across multiple files, it will start to slow down.

Hmm. I'll have to test these ideas.. I guess the real answer is: "IT Depends". :0

Friday, March 1, 2013

Understanding Hash, Sort and Exchange Spill events

Understanding Hash, Sort and Exchange Spill events:

'via Blog this'

CONCLUSION

Technically there is one more spill class: the spool spill. Since spools are *meant* to spill, the presence of a spool spill is usually less of a concern.
The purpose of this article is to show that there is ample documentation available on MSDN regarding these spill events. The tempdb spills are easily detectable and reasonably explained in the product documentation. Presence of spills may indicate potential performance problems as a spill involves disk reads and writes and is many times slower than the corresponding in-memory-only operation. They also add overload to tempdb and may cause contention.