Damon Payne: Hand waving software architect

103db signal to noise ratio at < .03% total harmonic distortion
Solution Architect, software developer, geek
Damon Payne at Blogged
2009 Microsoft MVP - Client App Dev
2007 Microsoft MVP - Solution Architecture
 Wednesday, May 30, 2007

Hosting a Designer Surface in Windows Forms Part 1

{if you are just tuning in, you may want to review:

Designer[0]

}

The most important things we’ll be working with is our class that implements System.ComponentModel.Design.IDesignerHost.  This class is the hub of many designer activities such as creating the designed components, Siting Components, and managing designer transactions.  In my case I went a slightly different route for transactions but this class is still very important.  Review the description (http://msdn2.microsoft.com/en-us/library/system.componentmodel.design.idesignerhost(vs.80).aspx) and a trivial sample implementation on MSDN.

Let’s take a look at a few lines of code after where we left off last, after LoadToolBoxItems() from the last article:

            //Setup the control that will hold all the template junk:

            _form = (BlankDesignForm)_designerHost.CreateComponent(typeof(BlankDesignForm));

            _form.Size = new Size(612, 792);

            _form.TopLevel = false;

            _form.Text = "Print Template";

            _form.FormBorderStyle = FormBorderStyle.None;

            _form.Font = new Font("Tahoma", 9);

            _baseFont = _form.Font;

            _form.Location = new Point(5, 5);

 

            _rootDesigner = (IRootDesigner)_designerHost.GetDesigner(_form);

            _designerView = (Control)_rootDesigner.GetView(ViewTechnology.Default);

            _designerView.Dock = DockStyle.Fill;

            _viewHostPnl.Controls.Add(_designerView);

Usually the under the cover plumbing interacts with the designer telling it what was selected in the IToolBoxService implementation and therefore what Type of Component to create.  We manually create a BlankDesignForm because it is the canvas that will contain the visual representation of all other designed Components.  Note that we can guarantee that the type of IDesigner created in this case is an implementation of IRootDesigner as opposed to IDesigner.  The first designed component is the Design Time Root.  An IRootDesigner, as opposed to other classes buried in the framework like System.Windows.Forms.Design.LabelDesigner gives us what we need to display the entire Designer Surface in our program, through its GetView() method.

The BlankDesignForm exists in order to gain some things specific to our problem domain such as drawing the dotted line around the page’s print margin you saw in the last article, but also to lose some functionality inherent to Windows Forms.  I’m talking about scaling.  While not explicitly related to the Designer Surface discussion I did run into this again during these articles and it’s a nice thing to know about anyway.

In order to be more Accessible, Windows Forms will try to scale the overall size of forms and their children in one of two ways in .Net 2.  The first is DPI scaling, meaning if the screen resolution changes from the design time resolution.  The second and more curious is font scaling.  If you have a Form that is 800x600 at design time with 8.25pt Microsoft Sans Serif, and at run-time you set its Font to be 9pt Tahoma, the form and all children will resize by roughly the difference between 8.25 and 9pt.  I say roughly because the behavior does not appear to be perfectly mathematically dependable in a transitive fashion.  If I go from 8.25 à 9 à 63 à 12 à 8.25 I will not end up with a form that’s quite right with respect to its original self.  The child controls may not be positioned in exactly their original places or the size may not be quite right.  I found workarounds for some situations but ultimately the resizing of ScrollableControl and its various viewports and bounds defeated me, it’s a long story for another article.  It’s too bad this doesn’t work, since if it did this would be a swell way to implement Zoom-In and Zoom-Out in pre-WPF applications.  This is all a very long winded way of saying that I needed my program to be somewhat skinable with colors, font family, font size, set at run time by processing a skin file.  Since the WYSIWYG-ness of my final printed document is dependent upon the convention that a 612pixel by 792pixel form represents the same space and proportions as an 8.5” by 11” document printed at 72dpi using my Compact Framework printer driver (http://www.fieldsoftware.com) a little Font scaling error is too much Font scaling error but the GUI skinning could not be given up.  The BlankDesignForm’s main contribution to my mental stability, then, is:

        protected override void ScaleControl(SizeF factor, BoundsSpecified specified)

        {

            //Don't scale!

        }

So the designed area where every pixel counts will not participate in the magical Scaling-based-on-font woo that takes place from within the bowels of Component.OnFontChanged.

In the code figure above everything is obvious except for our IDesignerHost.CreateComponent(Type)  implementation from the DefaultDesignerHost class. 

        /// <summary>

        /// Instructs the DefaultDesignerHost to instantiate a new instance of componentClass, which

        /// is also assumed to be added to the list of components that this DefaultDesignerHost is

        /// designing for through an IContainer implementation.  The component is Sited after listeners are given a chance to cancel this operation via

        /// the ComponentAdding method of the contextual IComponentChangeSerice instance.

        /// </summary>

        /// <param name="componentClass">valid Type</param>

        /// <param name="name">Component Name/Site Name</param>

        /// <returns></returns>

        public IComponent CreateComponent(Type componentClass, string name)

        {

            IComponent newComponent = (IComponent)Activator.CreateInstance(componentClass);

 

            IComponentChangeService changeSvc = (IComponentChangeService)_parent.GetService(typeof(IComponentChangeService));

            changeSvc.ComponentAdding(new ComponentEventArgs(newComponent));

 

            ISite site = new DefaultDesignSite(newComponent, this, name);

            newComponent.Site = site;

Any and all design time classes will attach to the events exposed by an IComponentChangeService implementation if they care about what goes on with Components at design-time. The first issue is the code in red.  It doesn’t work.  Take a look at the IComponentChangeService interface events:

 

Name

Description

ComponentAdded

Occurs when a component has been added.

ComponentAdding

Occurs when a component is in the process of being added.

ComponentChanged

Occurs when a component has been changed.

ComponentChanging

Occurs when a component is in the process of being changed.

ComponentRemoved

Occurs when a component has been removed.

ComponentRemoving

Occurs when a component is in the process of being removed.

ComponentRename

Occurs when a component is renamed.

 

… and the public methods …

 

Name

Description

OnComponentChanged

Announces to the component change service that a particular component has changed.

OnComponentChanging

Announces to the component change service that a particular component is changing.

 

Cruising MSDN shows that there is no public interface that forces any class to implement an OnComponentAdding or OnComponentAdded method.  While I don’t mind being left with a few design problems of my own to solve, this seems a little unfortunate since I am now forced to go outside the Design Time framework that’s been provided and implement something specific to my own Design Time environment.  It seems that it would be ideal to be able to give someone my ISelectionService or IComponentChangeService implementations without depending on behaviors that are outside the published contracts.  Of course I could just be missing something too :D.  Semantics being very important I chose the saucy name ITemplateDesignerComponentChangeService to represent my interface with the necessary methods added.  At least I didn’t just tack a “2” on the end of the original interface, yeah; I’m talking to you Sun.  Talk about lazy; nothing is worse than trying to determine if I should use SomeType or SomeType2.  Apparently this was considered a completely super practice in Java, but thankfully I don’t write Java anymore.

    interface ITemplateDesignerComponentChangeService : IComponentChangeService

    {

        void OnComponentAdding(ComponentEventArgs e);

        void OnComponentAdded(ComponentEventArgs e);

        void OnComponentRemoving(ComponentEventArgs e);

        void OnComponentRemoved(ComponentEventArgs e);

    }

I add an instance of a class implementing this new interface to my SerivceContainer.  Now if there are any classes buried in the framework that are not my own they will have access to an IComponentChangeService implementation for the purpose of event subscription, and my own code can ask for a Service Type implementing ITemplateDesignerComponentChangeService in order to access the extra methods.  The first few lines of our CreateComponent method now look like:

            IComponent newComponent = (IComponent)Activator.CreateInstance(componentClass);

 

            ITemplateDesignerComponentChangeService changeSvc = (ITemplateDesignerComponentChangeService)_parent.GetService(typeof(IComponentChangeService));

            changeSvc.OnComponentAdding(new ComponentEventArgs(newComponent));

 

            ISite site = new DefaultDesignSite(newComponent, this, name);

            newComponent.Site = site;

In the next article, we’ll start looking at more Service class implementations, where we tell the design-time framework about our Service implanting types, and the interactions between all of these pieces of the Designer Surface puzzle.



Wednesday, May 30, 2007 8:00:07 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

For a geek I sure don't get into many of the tech fads.  Ruby?  You can keep it.  iPod?  Not for me.  Zune, iPhone?  Whatever.

However, this, I must have.



Wednesday, May 30, 2007 9:21:08 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, May 29, 2007

As Larry points out some people just aren't down with using visual editors.  I'll extend this to say that the entire notion of the IDE doesn't resonate with everyone.  Ever since James Gosling proclaimed that "Anything serious is still done in emacs" the luddites have felt more comfortable in proudly claiming that an IDE such as VS2005 offers nothing that's not available from Vi+Shell Scripts.  I'm glad I don't work in that world anymore, but I suppose once you have convinced yourself that nothing new has been brought to the world of code editing in 20 years no Refactoring demo is going to change your mind.

Personally, I'd probably have to have a reference in front of me for creating WSDL but other than that I could hand-code anything I work with.  Editors and IDEs make me more productive.



Tuesday, May 29, 2007 11:42:01 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Sunday, May 27, 2007

In less than two weeks now, Jen and I are getting married.  Five years, a wonderful daughter, building a house, and a whole buch of other stuff have gone by but I'm surprised to be saying that getting married is steal a big deal.  We are "very secular" as anyone who's met me or read this blog for very long can attest but despite the lack of moral pressure to get hitched it seemed to make more and more sense as time went on for reasons that should be obvious.  Also, Jen likes diamonds.  I am currently recovering from my bachelor party about which I will say nothing but that while I consider myself Young still I cannot shrug off an event such as this without at least two days of healthy food and a lot of I am never drinking agains.  I suppose that phrase is the Hail Mary of the repentant imbiber.  Despite his odd notions about "sins" and other strange ethical constructs my brother and best man did a good job of throwing me my last party as an unmarried man.  I am currently developing grass roots support for the notion that the bachelor party should be a yearly occurance similar to celebrating the wedding anniversery.  The debate and voting on this bill seems to predictably fall along party lines but bi-partisan support can surely be gained in the usual political ways: bribes and favor trading or pork legislation burried in a must pass budget bill.  More information on the Payne-Payne Bachelor Reform initiative as it develops.

It looks like all of my long lost friends and respected colleagues that I had room to invite have responded in the affirmative so none of the "You are dead to me!" cards I created will have to go out.  We've got to squeeze in some dancing practice and a lot of final details but at this point my main concern is just making it through the day without some idiotic disastor like dropping the cake or spilling coffee on The Dress or something.  There's also the final Packing to consider.  My folks have some sort of worldwide timeshare thing that led to us getting a week in St. Lucia immediately after the wedding reception.  By immediately I mean we might have time for a 1 hour cat nap before heading down to O'Hare.  This will be the longest vacation I've ever taken and my first time out of the country (Mexicali does not count) so I'm sure there are any number of things that could go wrong but I'm not going to let anything get to me.  As long as we're not demolished by an early hurricane and our daughter can handle a week without Mom and Dad I'll call it good.  The sure-to-be ridiculous spoiling that awaits her with my parents will probably have her forgetting boring old Mom and Dad in no time.

In all seriousness, Jen is fantastic, she's freaking awesome.  What higher praise could a woman want from a nerd such as myself?  I'm lucky to be with someone who is happy to do the laundry (folding clothes is the one domestic skill I never gained competancy in) in exchange for my often experimental cooking.  I brag to my friends that she's almost beaten Zelda on the Wii for the second time.  She does her best to tolerate my Capitalist rantings at 1am after I've just finished a new economics book.  She loves it that I slowly absorb medical terminology such that she can complain about sepsis or a sub-dural hemotoma without also having to serve as a dictionary (it really ruines the flow of a story).  Are these the most important things in a mate? Maybe or maybe not, but its hard to offer examples to outsiders when you just have a rythm that works.  Also, Jen is hot.  We've been through a lot together and the future looks pretty good.



Sunday, May 27, 2007 10:42:23 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  |  Trackback

A respectable fellow recently asked me where I was on the Unit Testing spectrum: Skeptic <----> Zealot, or in between.  Fair question.

I am right-leaning towards zealot though far from what I'd call a unit testing zealot.  For one thing, I'm not into Test-first development.  I've heard the arguments enough times but its not a mental barrier I care to break down.  After years of struggling with laziness and mulling over the value of different approaches, this is a summary of my Unit Testing habits today:

  1. I use NUnit, TestDriven.Net, and NCover.
  2. I don't like unit testing strictly GUI aspects: Web Forms or Windows Forms, UserControl, etc.  I know that there ARE ways to do this but it doesn't resonate with me right now.  I somewhat dogmaticaly use Model-View-Presenter in ASP.Net and the desktop to minimize what is in the code behinds anyway.  I use NUnit.Mocks to work with Mock instances of my View interfaces.
  3. I do a lot of up front design before I write anything non-trivial but I do not create models at the Implementation level.  Writing the code is the first validation of my design, writing the unit tests is the second validation of my design.  Sorry for bludgeoning the deceased equine but if I can't get at method to test it and it also doesn't make sense to test said method through some other calling method, then there's likely a smell in the design.  I will typically revisit my preferred tool (currently StarUML) to see where the smell is, reimpliment, and take another stab at creating the appropriate tests.
  4. Unit tests are the most appropriate artifact you can leave behind for a project.  It seems to me that I run across very few people who do what I would call Real modeling and fewer who make an effort to update models as a system evolves.  I don't update models as a system evolves, but I do update unit tests.  The tests are only valuable as an artifact if they pass and if they can be shown to actually exercise the system.  See #5 regarding this.
  5. Code Coverage is the piece that finally made Unit Testing really click for me.  Forming the habit of using NCover directly from Visual Studio via TestDriven is the single largest improvement to the quality of my code, ever.  I find that I have difficulty inciting the same excitement about Coverage in other people, maybe because it forces one to admit they need to write more unit tests.  So you've tested everything you thought of on the first iteration and NCover shows only 80% coverage of your business logic assembly.  Sometimes you'll see you only tested the "Main success" flow through the code and you need to test the alternates and exceptions.  Sometimes you will see NCover highlighting code that is absolutely never going to be called from your application.  Many times I have caught "premature abstracting" using Ncover, classes or methods I assumed I'd need but nevered ended up needing.  This may not be a defect but I'm of the opinion that if I ever need it I'll recover it from version control so I delete unused methods/properties/classes to make my assembly smaller and compile faster.  Code Coverage is also a good aspect of a Code Review, if you're into those.
  6. I shoot for > 95% code coverage.
  7. While we work on stabalizing a release, testers sometimes find bugs!   As I look at each bug, I think, could this have been caught before acceptance testing if there'd been a specific unit test?  If I can think of a unit test for the bug situation, I add one.
  8. This one should be very obvious: I feel much better making huge changes to important code if there is a unit test suite.

 



Sunday, May 27, 2007 10:04:05 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Friday, May 25, 2007

Priceless:



Friday, May 25, 2007 9:50:22 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, May 23, 2007

I have seen several tutorials online on creating non-rectilinear windows in WPF using transparent images and no Background Brush.  Oddly enough this leaves you with the issue of not being able to grab the title bar (since it does not exist) and drag your curvey new window around.  I initially found no workable solution for this and even asked around to those who are wiser than I; no answer.  By rephrasing my request to google I found the following:

  1. Using Expression Blend, highlight your Window class.  Add an event handler for MouseLeftButtonDown
  2. In Visual Studio, add the following code to said event handler:

private void ExifViewer_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
   DragMove();
}

Fantastic.  The plumbing was already there, easy, and obvious, but because this is a new platform I could not find it.

.NET | WPF


Wednesday, May 23, 2007 3:00:30 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, May 20, 2007

So, Starcraft 2 is happening.  That's pretty sweet, though maybe not as geek lusty to some as a Diablo III announcement might have been.  I'm glad they did not try to turn this into a MMORPG.  Despite the game appearing to be fairly far along I can't fathom us seeing this before late 2008.  That will give me time to upgrade the desktop.



Sunday, May 20, 2007 11:39:24 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, May 18, 2007

If you haven't looked at WPF and Expression Blend yet it's important that you do so immediately.  The fact that I can do those cool effects from managed code is fantastic.  For example, I had a tool written for our support teams that would allow them to right-click on a JPG in Windows and choose "View Exif" from the menu.  We have customers (if you're a customer and you're reading this, I don't mean you, I mean someone else) who call us saying that their images didn't get associated with a vehicle and this way they can see the image metadata and if the scanner was used right.  I thought it would be cool if this could be in the shape of my company logo instead of a rectangular window, with some partial transparency.  Ignore my pan roasted broccoli with ginger and chilli sauce partially visible through the form.

My first few hours of experimentation with Expression Blend and WPF are very promising.  Besides creating this non-rectilinear window you can scale and rotate about anything and give the appearance of things being detached from your main window.  Behold my incredibly confusing rotated ListBox and detached close button.  The rotated ListBox shows the danger in this tool: remember things like the <blink> and <marquee> tags?  Bad ideas that looked cool for about five seconds and later made the bad-design hall of fame will be oh-so-easy for developers to unleash on unsuspecting users now.  Mitigating that risk is the fact that it appears seperation of the Designer's World from the Programmer's World is cleaner and more appropriate than many past attempts at supporting this separation of labor.  This aspect is most exciting to me: I would love to be able to tell our marketing partner who does our graphics to just go crazy with the user interface and email me the XAML so that I can work on putting functionality behind a top-notch design and not worry too much about us unintentionally trampling each other's work.  This is very, very cool.

.NET | Rant | WPF


Friday, May 18, 2007 3:03:42 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

I registered DamonPayne.net and pointed it to this site, in case you'd rather think of .net when you go to read about .net, or home theater, or politics, or see pictures of food, or something.

 

.NET | Rant


Friday, May 18, 2007 8:52:14 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

I've had some issues with Service Unavailable messages the past several days.  This appears to be related to me creating an Application below the main Application at my site root in order to test something.  This should be supported behavior so I am working on this with my hosting provider.



Friday, May 18, 2007 8:50:51 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, May 14, 2007

Oh, have I been busy.  This was the last weekend Jen has off before our wedding on June 9th so we were wisely utilizing it to goof off.  I cooked pancakes, Grilled shrimp skewers (sooo good), and homade chocolate mousse with strawberries.  Still, I have several articles in various stages of completion that I should be able to publish in the coming weeks:

  • A further discussion of ProjectEuler problems along with a Big Integer implementation
  • The next stage of the Designer Host discussion (Designer[1]) for .Net 2
  • Thoughts on the moral principles involved with advocating, using, and creating Open Source Software


Monday, May 14, 2007 10:06:58 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, May 07, 2007

http://arstechnica.com/news.ars/post/20070506-science-may-be-the-new-political-litmus-test-for-rationality.html

The potential GOP presidential nominees debate their stance on evolution.  What is the role Rational Thought should play in our lives?  I find it entertaining (and sad) that several candidates had to re-issue statements clarifying their positions.  It seems one ought to know what one's convictions are.



Monday, May 07, 2007 10:56:20 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

SOTO: Stay On Top Of.

Usage "I've got to SOTO the version 2.0 rollout to be sure its going well."

 



Monday, May 07, 2007 10:37:51 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, May 03, 2007

I'm sure you are often sitting at your new Vista desktop wondering "I wish I knew what the standard options and factory colors of a 2005 BMW M3 were and all I have is this VIN of my friend's M3."  Well, you may be in luck.  I have created a VINTool as a Vista desktop gadget, shown below.  Right now I'm just framing something in while a proper interface is created.  I have to make sure I'm cleared to release this, as its not 100% certain if there will be a licensing issue with some of our data.  In addition we may create some type of Vehicle notification so that you can say "I'm searching for a Triumph TR6 within 100 miles" and you'll see a message if such a thing is added to our systems.  Useful?  Useless but cool?  Neither useful or cool?  You decide.



Thursday, May 03, 2007 3:00:21 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Some of those thoughts I didn’t get to last night.

First of all I should say that the threading in .NET rocks in my opinion.  It’s fast, easy to program for, and very complete for a managed platform.   The inclusion of intra-process mutexen is especially nice.  So, here’s some code for utilizing some of the functionality I showed designs for yesterday.

            PrimeSieveContext ctx = new PrimeSieveContext(1000000);

 

            WorkDispatcher<long, PrimeSieveContext> facade = new WorkDispatcher<long, PrimeSieveContext>(3,ctx);

            PrimeAdderWorkUnit wu0 = new PrimeAdderWorkUnit(1, 500000);

            PrimeAdderWorkUnit wu1 = new PrimeAdderWorkUnit(500001, 750000);

            PrimeAdderWorkUnit wu2 = new PrimeAdderWorkUnit(750001, 1000000);

            facade.AddWorker(wu0);

            facade.AddWorker(wu1);

            facade.AddWorker(wu2);

<snip/>

long result = facade.Execute();

So when I said there’s a lot of room for improvement, the first item I’d like to look into is setting it up so that the WorkDispatcher decides for itself how many work units to split the problem domain into.  That is somewhat at odds with the first improvement I made.  Here’s a bit of the WorkDispatcher Execute method:

            if (_maxThreads > 1)

            {

                //Start beyond the 1st one, spawn threads, wait until they are all done

                for (int i = 1; i < _maxThreads; ++i)

                {

                    ThreadPool.QueueUserWorkItem(new WaitCallback(_workers[i].ExecuteAsync), _ctx);

                }

                TResultType rslt = _workers[0].Execute(_ctx);

 

                WorkerSharedContext<TResultType> finalCtx = _ctx as WorkerSharedContext<TResultType>;

                for (int i = 1; i < _maxThreads; ++i)

                {

                    _workers[i].CompleteHandle.WaitOne();                   

                    finalCtx.AddResultComponent(_workers[i].Result);

                }

                return finalCtx.GetContextualResult();

            }

First of all, I love the Generics in .NET 2 but several times I’ve ran across a situation where I wish one of the Template types could also be specified as implementing an interface or extending a base class.  I suppose I need to refactor the design here.   At any rate I noticed depending on which chunk of work I placed in the _workers[0] spot that I could keep both my CPUs maxed for longer and finish the work a few seconds sooner.    Ideally I should just give the WorkDispatcher hints about this rather than ordering the chunks myself, so I added the following:

        /// <summary>

        /// May cause the WorkDispatcher to re-order upon Setting this value.  The default value is FIFO

        /// </summary>

        public WorkDispatcherScheduleTypes ScheduleType

        {

            get { return _scheduleType; }

            set

            {

                _scheduleType = value;

                if (_scheduleType.Equals(WorkDispatcherScheduleTypes.OrderSensitive))

                {

                    _workers.Sort(new WorkUnitOrderComparison<TResultType, TSharedContext>());  

                }

                else if (_scheduleType.Equals(WorkDispatcherScheduleTypes.WeightSensitive))

                {

                    _workers.Sort(new WorkUnitWeightComparison<TResultType, TSharedContext>());

                    _workers.Reverse();

                }

The WorkUnits  now have assignable Weight and Order properties, no rocket science here.  Looking at the threading code in the previous figure we see where I am not yet meeting my stated goal of keeping the CPUs maxed as much as possible to complete the work using all the power available.  In this example with 3 WorkUnits and two physical processors the ideal would be to run two in parallel with the 3rd one being assigned to a physical processor as soon as one becomes available.   I read recently that this technique is being used with success in optimizing games for the PS3.  Neither ThreadPool.QueueUserWorkItem nor the framework I have here so far support any sort of event for saying “I’m done” so the WorkDispatcher can put the next WorkUnit on a physical CPU so I’ve got some more code to write.

Lastly, the PrimeAdderWorkUnit class is currently responsible for properly locking the pieces of the SharedContext it might be working with at any time.  It would be nice to create a mechanism for doing this automatically if such a means can be found in .NET.  It would be oh so nice to be able to overload the “.” or “->” operator.   I’ll revisit this when I can.

 



Thursday, May 03, 2007 9:42:39 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, May 02, 2007

Thanks to Mr. Heidenreich I lost most of my free time this afternoon.

I had posted about the programming problems on Project Euler and Mr. Gerry Heidenreich got addicted right away and we had been talking back and forth about some of our solutions.  He pings me to tell me that he is having dreams (nightmares?) about Problem 10 in the set.  It was taking too long to run, way too long since the problems should complete in less than one minute.  We both have almost the same computer oddly enough, a 2.13ghz Core 2 Duo.  If you’re too lazy to follow the link, the problem is to add the prime numbers that are less than 1 million.

I started with his algorithm and got the run time down to about 80 seconds with some plain code optimizations and expanding upon the Sieve he was using for eliminating factors of primes.  To be fair this took a while.  To be even more fair, there are probably Math-only optimizations that would eliminate the need for what you are about to see, but there is a larger lesson here.

I’ve been saying to anyone who’ll listen that chip-makers have been letting us down for about 3 years.  We can no longer expect significantly faster and faster sequential processors to speed up the more complex apps we are using and writing.  The Consolation Prize is ubiquitous multi-core chips in laptops, desktops, and servers.   I have engaged in various debates about the magnitude of the benefit of this approach, especially for Game Programming or other tasks where the solutions (at least the currently practiced and taught ones) are fundamentally sequential in nature.  I had been meaning to look into the cost of context switching, memory copying and synchronization primitives for problems that don’t immediately appear to lend themselves to SMP.  My mediocre math skills gave me a test problem. 

My first attempt was lightning fast but gave the wrong results due to an obvious synchronization issue.  I decided to give this problem at least a fraction of the thought it deserves and came up with some initial goals.  I wanted to make a class that can encapsulate a chunk of the work.  Some sort of controller class should organize the chunks of work, and be able to handle work broken up into arbitrary #s of slices.  The controller should be able to make sure all work is done before calculating the final solution.  To solve my obviously sloppy threading issues, tasks running concurrently should be able to safely share some sort of read/write context that may be necessary to manage dependencies between chunks of work or at least the final results.

To skip to the punchline, the multithreaded method using the pattern I came up with is almost twice as fast as the single threaded method, gets the correct result, and has given me food for thought on future problems requiring significant computing.  Almost twice as fast on 2 procs as on 1 is about as good as you can get so at least I got that part right.

Now, to rewind to the solution. The first thing that jogged my memory was watching the single-threaded method run.

CPU usage would not peg the CPU even when setting the process to Highest priority and turning off everything else that was running.  I know very little about the details of the Core 2 Duo but unlike hyperthreading its two real processors, so somehow either Windows, the BIOS, or Intel are spreading the load across two processors while half of my overall horsepower goes wasted.

Here is a model of the first iteration of my work-splitting solution:

 

WorkUnit made the most sense for the Command class name, not to be confused with other Unit-Of-Work patterns.

Obviously one goal is to make something I can keep refining as I come across new problems and have a .NET pattern/framework for speeding up execution of large tasks in the future.  The problem I’m solving here is probably one of the simplest examples of concurrent tasks with data interdependencies both constantly during execution was well as result assembly when all WorkUnits are done.  In this case the shared context is that all threads of execution will be reading and writing to an array of flags.  These flags mark numbers later on in processing as “Not possibly a prime candidate” when they are factors of already processed numbers.  The process in action looks something like this:

Now I ran with two WorkUnits, each sharing part of the range of possible prime #s and calculating their own sub-sums to be contributed to the final tally when all analysis was done.  I was happy to see a different CPU load…

… as well as obtaining the correct answer at the end and in far less time than single threaded solution.  I think there is still room for improvement, quite a bit in fact.  As soon as the one work unit is done the CPU load goes back down to 50% while the WorkUnit with the larger #s completes.

I have some other initial thoughts on this framework that I’ll get to later tonight, time permitting.



Wednesday, May 02, 2007 7:16:24 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Tuesday, May 01, 2007

I've been having an issue with Folding@Home on Vista, specifically that the error "Could not connect to Work Server (results)" often, maybe always, comes up when my desktop completes a work unit.  The solution in my case was to uncheck "Use IE connection settings", apparently there are some issues with Vista+IE7+FAH 5.0.3.  I wonder how much electricity I wasted...



Tuesday, May 01, 2007 6:57:42 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Someone asked me the following today (paraphrased)

"If I have versions 1.0.0.0 and 2.2.2.2 of TestAsssebly both in the gac, and a program TestApp.exe that needs TestAssembly, can I instantiate classes from each assembly inside the same app domain.  Can I use both versions of the Foo class in my program?"

So I sat down to do a simple experiment.  Two versions of Test Assembly are created in the GAC and a console program is run.  Foo.ToString() returns the full name of the assembly that the Foo instance comes from.

Program:

using System;

using System.Collections.Generic;

using System.Text;

using System.Reflection;

using TestAssembly;

 

namespace TestApp

{

    class Program

    {

        static void Main(string[] args)

        {

            try

            {

                Console.WriteLine("Loading Foo...");

                Assembly foo1 =

                AppDomain.CurrentDomain.Load("TestAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=612615ef384d0cd0, processorArchitecture=MSIL");

 

                Foo f = new TestAssembly.Foo();

                Console.WriteLine("Typeof Foo: " + f.ToString());

 

                object f2 = foo1.CreateInstance("TestAssembly.Foo");

                Console.WriteLine("Typeof f2: " + f2.ToString());

 

                Foo f3 = (Foo)foo1.CreateInstance("TestAssembly.Foo");

                Console.WriteLine("Typeof f3: " + f3.ToString());

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex);

            }

        }

    }

}

Output:

C:\Projects\StrongNameTest\TestApp\bin\Debug>TestApp.exe
Loading Foo...
Typeof Foo: TestAssembly.Foo TestAssembly, Version=2.2.2.2, Culture=neutral, Pub
licKeyToken=612615ef384d0cd0
Typeof f2: TestAssembly.Foo TestAssembly, Version=1.0.0.0, Culture=neutral, Publ
icKeyToken=612615ef384d0cd0
System.InvalidCastException: Unable to cast object of type 'TestAssembly.Foo' to
 type 'TestAssembly.Foo'.
   at TestApp.Program.Main(String[] args) in c:\Projects\StrongNameTest\TestApp\
Program.cs:line 25

The part in bold is entertaining, at least.  I have a strong suspician that if I interact with f3 as an Object only, using Reflection to Invoke properties and methods then I could in fact use all the version 1.0.0.0 Foo functionality (which is vast and important) within the same app domain.

I also hit a bit of a snag when testing this application.  I was previously unaware that Visual Studio does not let you add references to assemblies added to the GAC via gacutil on the local machine.  See: http://msdn2.microsoft.com/en-us/library/wkze6zky(VS.80).aspx

 



Tuesday, May 01, 2007 1:29:02 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback