Monday, December 22, 2008

Getting The Current Mouse Position The Windows Presentation Foundation

While working on a Windows Presentation Foundation post now and I ran across an oddity between Windows Forms and WPF. The System.Windows.Input.MouseEventArgs object does not contain the current position of the mouse.

In the Windows Forms world, the event arguments contained the cursor position. You could do something to the effect of:
protected void image1_Click(object sender, MouseEventArgs e)
{
    int x = e.X;
    int y = e.Y;
    // Do something with the coordinates

}

Leaving the Win32/Windows Forms world behind, things change in WPF. In WPF, the MouseEventArgs actually points to the System.Windows.Input.MouseEventArgs object and not the System.Windows.Forms.MousEventArgs object.
protected void image1_Click(object sender, MouseEventArgs e)
{
    FrameworkElement control = (FrameworkElement)sender;
    Point MousePos = Mouse.GetPosition(control);
    
}
Or we can use the event like this:
protected void image1_Click(object sender, MouseEventArgs e)
{
    Point position = e.GetPosition(this);
}
So, this is great, we can find the position of the mouse again.

Taking this a bit farther, what if we were upgrading a windows forms application to WPF and wanted to minimize the code we would have to change?

HINT: there is no drop in replacement for it.

I will go through some of the mental gymnastics and share some possible solutions with the implications.

It might be nice to extend the MouseEventArgs. This currently is not possible because the concept of "extension properties" is not implemented. Perhaps in .Net 4.0. We could implement something similar with extension methods requiring the use of parenthesizes, which would not be a drop-in replacement. That leaves us with 2 options, 1) Use partial class to add the properties and 2) play the namespace game and create our own class that inherits the System.Windows.Input.MouseEventArgs.

We can rule out the partial class because the System.Windows.Input.MouseEventArgs object is not a partial class. So the other options have failed to bare fruit, it appears that we would have to fall back to the old faithful object oriented convention of programming, inheritance. We could produce the following:
namespace System_Windows_Input2
{
    public class MouseEventArgs : System.Windows.Input.MouseEventArgs
    {
        private Point currentPosition;
        private bool isLoaded;
        private Point CurrentPosition {
            get{
                if (!isLoaded) {
                    isLoaded = true;
                    FrameworkElement control = (FrameworkElement)Source;
                    currentPosition = Mouse.GetPosition(control);
                }
                return currentPosition;
            }
        }
        public double X
        {
            get {
                return CurrentPosition.X;
            }
        }
        public double Y
        {
            get
            {
                return CurrentPosition.Y;
            }
        }
    }
}
As long as the namespaces are listed in the correct order, System_Windows_Input2.MouseEventArgs will shadow the System.Windows.Input.MouseEventArgs. I.e.:
using System.Windows.Input;
using System_Windows_Input2;
Success! Right? We must ask ourselves, "is this way worth it?" and "are there unexpected consequences?". Myself, I would have to be really hard up and in a hole to seriously consider this as a possibiility. This really just buys us a small amount of updating to the mouse event handlers and in all probability the application is not going to have an overly significant amount of them. Worse, it breaks the new standard way of getting the current mouse position; meaning that maintainability goes down.

If we use System.Windows.Input.MouseButtonEventArgs, System.Windows.Input.MouseWheelEventArgs, System.Windows.Input.QueryCursorEventArgs objects, they will not contain the X and Y properties because they inherit from the System.Windows.Input.MouseEventArgs, not our modified class; we'll call this an unexpected consequence. To achieve the the same effect as our System_Windows_Input2.MouseEventArgs class, we would have to implement the same game with the other classes. Leading to more code, more bloat, and less maintainability.

So, we can implement the same functionality we had in the Windows Forms world, but in the end gains don't out weigh the downsides. It will likely take more code to re-implement the old way than to just update the code to the new standard way of doing things. Happy coding!

Monday, November 3, 2008

Navigating Text Boxes In A Grid View With Arrow Keys

To many business people/non-developers, Excel is the best application ever. It's a database, a calculator, a data entry system, and so much more. I have to hold myself back from laughing when I hear phrases like "Excel database" or "Excel application". In fairness, Excel is a flexible, powerful application and it is easy for lay-people to bend to their needs.

Problems arise when a developer is tasked with taking a process and develop it for the web. The web paradigm does not lend itself to reproducing Excel very easily, sometimes not at all.

In my situation, Excel really wasn't a part of the business' original process, but they are most familiar with it. We came up with several ideas and concepts using standard grid view conventions. They were turned down because they were not "Excel". After several conversations to try to get at what exactly the business users did not like (which they could not immediately communicate), it finally came down to the fact that they wanted Excel in a web page.

Since we are using Microsoft Office SharePoint Services, it seemed clear that Excel Services would be the ideal solution. Not having used it before, I did some further investigation and determined that there was much to be desired.

I was forced to go back to the grid view. After more conversations it came to light what functionality we needed to satisfy the business users needs. For the amount of data and entry speed they were looking for, the standard way of navigating the controls was no acceptable. To increase the speed of navigating around the grid, I decided that we could implement arrow key navigation similar to Excel.

Goals & enhancements:
  • Make the arrow key navigation work similar to Excel
  • If a column is not editable, skip it and proceed to the next column with input
  • If the caret is at the edge of the table, wrap around to the other side
  • Make the solution generic so it can be applied to any grid
  • Use javascript objects
  • Use as little code as possible and optimize for the code to be Minified.
So we have our requirements, now lets create our solution. This is a work in progress. I am sure there is much room for improvement, feel free to suggest optimizations.

In the javascript include file:
var Grid = function(table) {
    var obj = (typeof table == 'string') ? $("#"+ table)[0] : table;
    this.length = obj.rows.length;
    this.parent = obj.parentNode;
    this.tbl = obj;
    this.cellLength = (this.length > 0) ? this.row(1).cells.length : 0;
};
Grid.prototype = {
    row: function(idx) {
        return this.tbl.rows[idx];
    },
    cell: function(rowIdx, idx) {
        return this.tbl.rows[rowIdx].cells[idx];
    },
    XYByEl: function(el) {
        var td = ((el.tagName=="TD") ? 
            el : 
            (el.parentNode.tagName=="TD") ? 
                el.parentNode : 
                null);
        return (td) ? 
            {r: td.parentNode.rowIndex, c: td.cellIndex} : 
            {r: 1, c: 0};
    },
    firstChild: function(rowIdx, idx) {
        return this.firstInput(this.cell(rowIdx, idx));
    },
    firstInput: function(c) {
        if (c!=null)
            if (isIE && (c.children[0]!=null)&&( ( (c.children[0].tagName=="INPUT") && ( (c.children[0].type=="text")||(c.children[0].type=="checkbox") ) ) || (c.children[0].tagName=="SELECT"))) {
                return c.children[0];
            } else {
                var cn = c.childNodes;
                if (cn) {
                    for (var idx=0; idx < cn.length; idx++) {
                        if ( (cn[idx].nodeType == 1)&&( ( (cn[idx].tagName=="INPUT") && ( (cn[idx].type=="text")||(cn[idx].type=="checkbox") ) ) || (cn[idx].tagName=="SELECT")) ){//
                            cn[idx].setAttribute('autocomplete','off');
                            return cn[idx];
                        }
                    }
                }
            }
        return null;
    },
    nextInput: function(r, index, adder) {
        var fi = this.firstInput(r.cells[index]);
        if (fi)
            {return this.focusSelect(fi);}
        else if (index+adder >= r.cells.length)
            return this.nextInput(r, 0, adder);
        else if (index+adder < 0)
            return this.nextInput(r, this.cellLength-1, adder);
        else
            return this.nextInput(r, index+adder, adder);
    },
    focusSelect: function(ctl) {
        if (ctl) {
            ctl.focus();
            if (ctl.type=="text"){ctl.select();}
        }
        return ctl;
    },
    cellUp: function(idx, cIdx) {//wrap to bottom when at top
        idx = (idx-1 === 0) ? this.length-1 : idx - 1;
        var newctl = this.firstChild(idx, cIdx);
        return this.focusSelect(newctl);
    },
    cellDown: function(idx, cIdx) {
        var newctl = null;
        if(idx+1 == this.length) { //wrap to top when at bottom
            newctl = this.firstChild(1, cIdx);
            this.parent.scrollTop = 0;
        } else {
            newctl = this.firstChild(idx + 1, cIdx);
        }
        return this.focusSelect(newctl);
    },
    cellLeft: function(idx, cIdx, child) {
        if(caretAtBegin(child)) {
            var newctl = null;
            if (prevKeyUp == 37) {
                newctl = this.nextInput(this.row(idx),cIdx-1, -1);
                if (!caretAtBegin(newctl)) prevKeyUp = -1;
                return this.focusSelect(newctl);
            } else {
                prevKeyUp = 37;
            }
        }
    },
    cellRight: function(idx, cIdx, child) {
        if(caretAtEnd(child)) {
            if (prevKeyUp == 39) {
                if(cIdx == this.cellLength-1) 
                    this.parent.scrollLeft = 0;
                newctl = this.nextInput(this.row(idx),cIdx+1, 1);
                if (!caretAtEnd(newctl)) prevKeyUp = -1; 
                return this.focusSelect(newctl);
            } else {
                prevKeyUp = 39;
            }
        }
    }
};
function getTarget(ev) {
    var t = ev.target || ev.srcElement;
    return (t && 1 == t.nodeType) ? t : null;
}
var prevKeyUp = -1;
function ArrowKeyNav(gridTable, e) {
    if(!e) e=window.event;
    var key = e.keyCode;
    if (/^(?:3[7-9]|40)$/.test(key)) {
        var table = new Grid(ScrollTable),
            targ = getTarget(e),
            newctl = null,
            cellInfo = table.XYByEl(targ),
            Index = cellInfo.r, childIndex = cellInfo.c;
        if (cellInfo)// != null 
            switch(key) {
                case 40: //down
                    newctl = table.cellDown(Index, childIndex);
                    break;
                case 38: //up
                    newctl = table.cellUp(Index,childIndex);
                    break;
                case 37: //left
                    newctl = table.cellLeft(Index, childIndex, targ);
                    break;
                case 39: //right
                    newctl = table.cellRight(Index, childIndex, targ);
                    break;
                default:
                break;
            }//end switch
        return newctl;
     }//if eventKey
}//end fcn 

caretPos = function (control) {
    var iCaretPos = 0;
    if (control){//!=null
        if (document.selection) { //IE Support
            var oSel = document.selection.createRange();
            oSel.moveStart('character', -1*(control.value.length));
            iCaretPos = oSel.text.length;
        } else if (control.selectionStart || control.selectionStart == '0')
            //Firefox Support
            iCaretPos = control.selectionStart;
    }
    return iCaretPos;
};
caretAtEnd = function(control) {
    return ( (control) && ( (control.type=="checkbox")||(control.tagName=="SELECT") ) ) || (caretPos(control) >= control.value.length);
};

caretAtBegin = function(control) {
    return ( (control) && ( (control.type=="checkbox")||(control.tagName=="SELECT") ) ) || (caretPos(control) == 0);
};
In the grid page, put a script block with the following in it:
$(document).ready(function(){
    $("table.NavWithArrows").keyup(function(e){
        return ArrowKeyNav([table client id], e);
    }); 
});
This and a couple other tweaks, satisfied the business' need. The class NavWithArrows would be attached to the grid view table. This code catches the on key up event when it bubbles up the DOM to the table element.

As it turns out, there is a considerable amount of code to make this functionality happen. Hopefully this will save someone many hours of programming and debugging.

The applications of this go beyond a simple grid application. With a few tweaks, this solution can be applied to a genral input form or extended to allow more than one input control in grid cell(this version assumes there is only one control per cell). I'll leave it to others to extend this functionality.

Friday, October 31, 2008

Leave Page Without Saving Warning

Let's say that you have a web application in which people enter a lot of information and to make sure they don't loose any information before they leave the page or close the browser by warning them that something has changed and not saved. This code uses jQuery, but is not jQuery dependent, it is just a way for me to quickly develop. for example you can use any function to set the beforeunload event with the function.

var _FormModified_ = false;
$(window).bind("beforeunload", function(e) {
    if (_FormModified_) {
        var msg = "\r\nInformation has changed.\r\nAre you sure you want to leave without saving?\r\n";
        (e || window.event).returnValue = msg;
        return msg;
    }
});

var FlagModify = function(e) {
    if (!(/^(?:9|1[6-9]|2[07]|3[3-9]|4[04-5]|9[13]|11[2-9]|12[0-3]|14[45])$/.test((e||window.event).keyCode))) 
        {_FormModified_ = true;}
};
Then on any page you want to warn on you can add this code:
$(document).ready(function(){
    $("div.inputForm input").keyup(function(e){
        FlagModify(e);
    });
    $("div.inputForm select").change(function(e){
        FlagModify(e);
    }); 
});
A bonus you get with this way of binding is that you can specify and scope which input form areas to watch. This allows you to exclude search boxes or other controls that don't need to be saved.

If AJAX or UpdatePanels are used to save the information, a client onclick event must be added which sets _FormModified_ to be false.

A Simple, Flexible Architecture For Popup Warnings

I have looked around the web for a good way to display on screen warnings and found most examples wanting. They were rather bloaty, required the use of a different library or you had to pay for them. So I set out and with my favorite javascript library, jQuery, and made my own.

My goals were pretty simple:
  • I wanted to use built-in capabilities when I could (e.g. the tooltip/hover title/alt)
  • Use jQuery to speed up the development and make it easily cross-browser compatible
  • Make it small, compact, and as few lines as possible
  • Assume no needed html will be on the page calling the function
This is still a work in progress but it is completely functional in it's current state. In my CSS file:
.popup{
    border:1px solid #CCC;
 position:absolute;
 width:250px;
 border:1px solid #c93;
 background:#ffc;
 padding:5px;
 right:3px;
 top : -175px;
 font-weight:bold;
}
.popup div p
{
 font-weight: normal;
 padding: 3px;
 margin: 0px;
}
.nohref, .popup div p {
 cursor: pointer;
 cursor: hand;
 text-decoration: underline;
}
In my javascript file:
var getScrollXY = function() {
    var w=window, db=document.body, dde=document.documentElement;
    return ( typeof( w.pageYOffset ) == 'number' )          ? [w.pageXOffset, w.pageYOffset] :
            ( db && ( db.scrollLeft || db.scrollTop ) )     ? [db.scrollLeft, db.scrollTop] :
            ( dde && ( dde.scrollLeft || dde.scrollTop ) )  ? [dde.scrollLeft, dde.scrollTop] : [0, 0];
};
varClearCtlBg = function(ctl) {
    ctl.style.background = "#FFF";
    ctl.setAttribute('title', "");
};
var Validate = function (ctl, rule, msg, idsuffix) {
    if (rule && msg && (msg.length>0)) {
    var ctlid = ctl.id+idsuffix;
    ctl.style.background = "#FFF url(http://intellectualponderings.googlecode.com/svn/trunk/blog/images/invalid_line.gif) repeat-x scroll center bottom";
    cTitle = ctl.getAttribute('title');
    if (cTitle.indexOf(msg) == -1) {
        ctl.setAttribute('title', ((cTitle) ? cTitle + "\r\n[ " : "[ ") + msg);
    }
    var warning = "<p id=\"" + ctlid + "_Warn\" onclick=\"focusSelect('" + ctl.id + "');\">" + msg + "</p>";
    if ($("#WarnMsg").length === 0) {
        $("body").append("<div class=\"popup\" id=\"WarnMsg\"><a class=\"nohref\" style=\"float: right;\" onclick=\"$('#WarnMsg').fadeOut('slow').remove();\">X</a>Warning<div>"+ warning +"</div></div>");//
        $("#WarnMsg").show().animate({ top: String(Number(getScrollXY()[1])+3)+"px" }, 750 );//-175
        //$("#closeMessage").click(function() {$("#WarnMsg").fadeOut("slow").remove();}); id=\"closeMessage\" style=\"display: none;\"
    } else if ($("#"+ctlid+"_Warn").length > 0) {
        $("#"+ctlid+"_Warn").fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100);
    } else {
        $("#WarnMsg div").append(warning);
    }
    setTimeout("ClearWarning('"+ctlid+"_Warn')", 15000);
    return true;
    }// Validate - if (rule)
};
var focusSelect = function(ctlid, e) {
    ctl = $("#"+ctlid)[0];
    ctl.focus();if (ctl.type=="text"){ctl.select();}
};
var ClearWarning = function(id) {
    $("#"+id).remove();
    if ($("#WarnMsg div p").length === 0) {
        $("#WarnMsg").hide("slow").remove();
    }    
};

// Lets add a warning function
var required = function(e) {
    ClearCtlBg(this);
    Validate(this, true, "This file is required.");
    Validate(this, (this.value.length > 3), "This file is required.");
};
In my html, I can do something simple:
<html>
<head>
    <!-- Include CSS file -->
    <!-- Include jQuery file -->
    <!-- Include Javascript file -->
</head>
<body>
    <input type="text" id="txtName" class="name" />
    <script type="text/javascript">
    $(document).ready(function(){
        $(".name").blur(required);
        //$(".someddl").change(ddlvalidator);
    });
    </script>
</body>
</html>
To trigger the warning, click in the text box below and click outside the text box. A popup should slide into view on the upper right hand corner. This can be customized by editing the .animate({ top: String(Number(getScrollXY()[1])+3)+"px" }, 750 );.


For example:

It may seem weird to use jQuery to attach a warning to one control. The logic behind this architecture is for use on input tables or Grid Views where every control in a column will have the same class name and each needs to be evaluated.

Saturday, October 25, 2008

Why isn't that function just there?... And how to fix that: become a language designer

Have you ever dug for a function you thought should just be there only to find out that it's not? Leaving you to create your own class inheriting the class that should have had the function in the first place. If you are in the middle of a large project and run across something like this, in .Net 2.0, your options are limited:
  • Creating a custom class that inherits the class you want to extend and add your function. This would force you to update all declarations of that type
  • Creating a shared function in a common static class. This can add make for some ugly code that doesn't look very object oriented
I was talking to some people a while ago about this very situation and I brought up the topic of extension methods. They responded with "what's that?" apparently never having heard of them. If they didn't know about them, I figure there are a lot of people out there who might want to be enlightened, thus the reason for this blog.
Extension methods, in .Net 3.0 (or later), add a third option to the list. They allow a function to attatch to all existing objects of a type. This enables anyone to become a language designer and fix all of the holes Microsoft left just to annoy you. Lets take a look at a couple examples.

Suppose a legacy system requires a CSV input and the .Net application contains a list of strings. The .Net 2.0 solution might look something like this:
public static class Common
{
  public static string ToCSV(List<string> list, char delimiter)
  {
    StringBuilder sb = new StringBuilder();
    for (int idx = 0; idx < list.Count; idx++) {
      sb.Append(delimiter); }
      sb.Append(list[idx].ToString());
    }
    return sb.ToString();
  }
}
...
// Then run
...
string csv = Common.ToCSV(<nameoflist>, ',');
This is all well and good; the code creates the expected CSV. Programming in this fashion breaks doesn't look very object oriented which is the goal of the language. If there are many references to this function, wrapping the list class and extending might by a lot of work and could be overkill. Using an extension method will produce an example like this:
public static class Extensions
{
 public static string ToCSV(this List<string> list, char delimiter)
 {
     StringBuilder sb = new StringBuilder();
     for (int idx = 0; idx < list.Count; idx++) {
         sb.Append(delimiter); }
         sb.Append(list[idx].ToString());
     }
     return sb.ToString();
 }
 public static void FromCSV(this List<string> list,
                             string csv, char delimiter)
 {
     list.AddRange(csv.Split(new char[] { delimiter }));
 }
}

class Program
{
 static void Main(string[] args)
 {
     List<string> strings = new List<string>();
     strings.Add("Bob");
     strings.Add("Jane");
     strings.Add("Jack");
     strings.Add("Mike");
     strings.Add("Mary");
     // Call the Extension Method
     string csv = strings.ToCSV(',');
     Console.WriteLine(csv);

     // Lets verify that the opposite way works
     List<string> newlist = new List<string>();
     // Call the Extension Method
     newlist.FromCSV(csv, ',');
     for (int idx = 0; idx < newlist.Count; idx++)
     {
         Console.WriteLine(newlist[idx]);
     }
 }
}
In my previous post, Manycores and the Future, I used an extension method on the Stopwatch class to better handle timing test code.

Extension methods are a powerful tool for creating utility libraries and adding common functionality to built-in or inaccessible object types. Like the adage, with great power comes great responsibility, this tool requires great responsibility. These methods can make maintaining the code more difficult for new maintainers. A developer can change inner workings and/or the output of the method, resulting in a debugging nightmare. Trying to locate the problem can become more difficult if someone assumes that the method is not built-in and the error is not explicitly in the method or at the call.

It would advisable to use extension methods for only generic cases where the method would be used in many different projects.

Now, lets take a section of the previous posts code and extend it to use a new "Quad" extension method. I'll discuss it further in a moment.

public static class Extensions
{
 /// <summary>
 /// Stopwatch Extension Method:
 /// Starts, executes function over iterations
 /// Returns the time span.
 /// </summary>
 public static TimeSpan Time(this Stopwatch sw,
                             Action func,
                             int times)
 {
     sw.Reset();
     sw.Start();
     for (int idx = 0; idx < times; idx++) { func(); }
     sw.Stop();
     return sw.Elapsed;
 }

 public static double[] Quad(this double[] p)
 {
     double[] x = new double[2];
     double a = p[0], b = p[1], c = p[2];
     x[0] = ((b * -1) + Math.Sqrt(b * b + 4 * a * c))/(2 * a);
     x[1] = ((b * -1) - Math.Sqrt(b * b + 4 * a * c))/(2 * a);

     return x;
 }
}

class Program
{
 static void Main(string[] args)
 {
     Stopwatch stopwatch = new Stopwatch();
     int max = 1000000;
     double[][] numbersP = new double[max][];
     double[][] numbers = new double[max][];
  
     Random rand = new System.Random(1042);
     Parallel.For(0, max, x =>
     {
         double[] item = new double[3];
         item[0] = rand.Next(500);
         item[1] = rand.Next(500);
         item[2] = rand.Next(500);
         numbers[x] = item;
     });

     TimeSpan ts = stopwatch.Time(() =>
     {
         Parallel.For(0, max, x =>
         {
             // Make the call to the extension method
             numbersP[x] = numbers[x].Quad();
         });
     }, 1);
     Console.WriteLine(
     String.Format("Parallel.For RunTime: {0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds/10));

     Console.Read();
 }
}
Basically, I just moved the Quad function to the static class (like with the first example) and changed the Quad(numbers[x]) to numbers[x].Quad(). The bonus here is less typing, looks more object oriented, and the "Quad" function appears in the intellisense allowing for easier finding of the functionality needed. Lets assume for a moment that Quad is a very useful function and that we would want to use over and over. We must ask ourselves if we want to have this function available to all developers to have access to this function who create a variable of type double[]. There are many problems with attaching methods to arrays and doing things like this. If someone passes in an array with size greater than 3, it's use is likely not what the caller thinks it is and the caller won't know there is a problem until farther down the execution path. Another problem arises when an array of size less than 3 is passed in. These errors are raised at run time. Compile time errors are always better than run time.

Using Quad in this fashion is definitely not an optimal approach/reason to using extension methods. In this case Quad does not really extend the type double[], therefore one of the other options listed above is definitely better. For example, adding this function to a common math class. This will be the hard part for new developers to understand and adds to my reasoning for using them sparingly and after some diligence.

This will be an important tool for experienced programmers. This has the makings of many sleepless nights for me wondering when I am going to misuse of extension methods like the example above.

Sunday, October 12, 2008

Manycores and the Future

Back in June, Microsoft released a preview of the .Net 4.0 framework for .Net 3.5 called "Parallel Extensions". A huge advancement in the framework is a realization that, with the huge take up of manycore systems, many programmers are not taking advantage of the available resources.

For many programmers threading is either 1) a complex, unknown world, 2) a scary, once bitten thing, 3) a gift that comes naturally. Concurrency is going to be a main point of enhancement in the new framework. It is the way of structuring an algorithm to make use of otherwise wasted clock-cycles by using threads to accomplish tasks which might take a while with available downtime.

In many cases using threading creates a complex situation. Keeping track of resources and fixing cross threading code is a daunting task. Setting up each thread and setting up resources for the thread can cause some ugly code and draw the programmer to focus more on operating system architecture. The extensions gives developers several different tools to allow them to use many cores without having to deal with threads and focus on the algorithm.

So, now thread handling is up to the compiler, concurrency is a walk in the park, right? Not so fast, now that we can focus more on the algorithm, we are still left understanding what is going on under the covers. The developer needs to be able to tell which parts of the algorithm can be done in parallel. Not all situations can benefit from or use parallelized code. There are several design patterns to consider. Is the work to perform mutually exclusive, i.e. the work at one step is independent of all other work done at the same time? Then it is an excellent candidate for being parallelized. Parallelizing code allows developers to take independent, repeated sections of code and run them concurrently.

The Parallel Extensions gives us a couple new looping constructs: Parallel.For, Parallell.For≶>, Parallel.ForEach≶>.

Today I am going to introduce you to Parallel.For. This new loop, takes in an anonymous delegate or in my case a lambda expression and concurrently runs the expression based on the supplied parameters.

When using the Parallel Extensions you must add a reference to the System.Threading assembly (it should be pointing to the GAC'ed assembly from the Parallel Extensions directory). If you forget to add this, you will quickly find that Parallel. doesn't exist.

The example, the Quadratic Equation:
public static class Extensions
{
   /// <summary>
   /// Stopwatch Extension Method:
   /// Starts, executes function over iterations,
   /// Returns the time span.
   /// </summary>
   /// <param name="sw">this</param>
   /// <param name="func">Delegate or lambda to run</param>
   /// <param name="times">Times to run the function</param>
   /// <returns>TimeSpan object of elapsed time</returns>
   public static TimeSpan Time(this Stopwatch sw,
                               Action func,
                               int times)
   {
       sw.Reset();
       sw.Start();
       for (int idx = 0; idx < times; idx++) { func(); }
       sw.Stop();
       return sw.Elapsed;
   }
}
class Program
{
   static double[] Quad(double[] p)
   {
       double[] x = new double[2];
       double a = p[0], b = p[1], c = p[2];
       x[0] = ((b * -1) + Math.Sqrt(b * b + 4 * a * c))/(2 * a);
       x[1] = ((b * -1) - Math.Sqrt(b * b + 4 * a * c))/(2 * a);

       return x;
   }

   static void Main(string[] args)
   {
       Stopwatch stopwatch = new Stopwatch();
      
       int max = 1000000;
       double[][] numbersP = new double[max][];
       double[][] numbersS = new double[max][];
       double[][] numbers = new double[max][];
      
       Random rand = new System.Random(1042);
       Parallel.For(0, max, x =>
       {
           double[] item = new double[3];
           item[0] = rand.Next(500);
           item[1] = rand.Next(500);
           item[2] = rand.Next(500);
           numbers[x] = item;
       });

       // Synchronous
       TimeSpan ts = stopwatch.Time(() =>
       {
           for (int x = 0; x < max; x++)
           {
               numbersS[x] = Quad(numbers[x]);
           }
       }, 1);
       Console.WriteLine(
           String.Format(
             "Synchronous RunTime: {0:00}:{1:00}:{2:00}.{3:00}",
             ts.Hours, ts.Minutes,
             ts.Seconds, ts.Milliseconds / 10));
   
       // Parallel For
       ts = stopwatch.Time(() =>
       {
           Parallel.For(0, max, x =>
           {
               numbersP[x] = Quad(numbers[x]);
           });
       }, 1);
       Console.WriteLine(
           String.Format(
             "Parallel.For RunTime: {0:00}:{1:00}:{2:00}.{3:00}",
             ts.Hours, ts.Minutes,
             ts.Seconds, ts.Milliseconds / 10));

       Console.Read();
   }
}
So what is the verdict?

Test Results

TypeTest 1
Debug
Test 2
Release
Test 3
Release
Synchronous00:00:01.1100:00:00.9700:00:00.96
Parallel00:00:00.7800:00:00.7500:00:00.74

This is quite a simplistic example involving solving many the quadratic equations for random numbers. The results show that there is a significant improvement in performance without much effort. Further more, we could use this idea to improve a whole host of scenarios like image rendering, validation, and making web requests.

Happy coding!

Saturday, October 11, 2008

Statement of Purpose

I suppose the place to start a first post is with a statement of purpose. This blog's intentions are to promote thinking and education in programming, science, technology, and philosophy. Many posts on here will be cross posted on my employer's blog. Since my employer's blog won't allow me to update bugs in the content, I am resorting to creating my posts here and then posting them on the companies blog.

Check out http://blog.apterainc.com/ for great software development posts.