SharpDevelop Community

Get your problems solved!
Welcome to SharpDevelop Community Sign in | Join | Help
in Search

Laputa

This is a mirror of http://laputa.sharpdevelop.net/, the core team's central news blog.

September 2007 - Posts

  • Choosing the target framework in SharpDevelop 3.0

    Visual Studio 2008 uses MSBuild 3.5 which supports multi-targeting: you can use it to compile applications for .NET 2.0, .NET 3.0 and .NET 3.5.

    But what happens if you open an MSBuild 2.0 project (Visual Studio 2005 or SharpDevelop 2.x project) in Visual Studio 2008?
    Answer: The “Visual Studio Conversion Wizard” will pop up and “convert” the project to MSBuild 3.5. Though it does generate a fancy upgrade report that looks like source files have been converted, all it does for a normal Windows Application is the following:

    • In the .sln file, the solution version is set from 9.00 to 10.00, and the “# Visual Studio 2005” line is replaced with “# Visual Studio 2008”. This causes the Visual Studio loader to run VS 2008 instead of VS 2005 when the .sln is double-clicked.
    • In the .csproj file, the attribute ToolsVersion=”3.5” is inserted. This causes MSBuild to use the C# 3.0 compiler.
    • The property “<OldToolsVersion>2.0</OldToolsVersion>” is inserted, and two properties FileUpgradeFlags and UpgradeBackupLocation are introduced, but not set to a value. This has no effect on compilation.
    • In the .csproj file, the Resources.Designer.cs file got marked as “<DesignTime>True</DesignTime>”, which has no effect on compilation.
    • All files generated by a custom tool (Resources.Designer.cs and Settings.Designer.cs) get regenerated.
    • All other source files remain unchanged.

    Note that even though the C# 3.0 compiler is used, the project is still compiled for .NET 2.0. The new C# 3.0 languages features are available, but LINQ cannot be used because it requires referencing System.Core.dll, which comes with the .NET Framework 3.5.
    So the converter did only minor changes, but they prevent working with both VS 2005 and VS 2008 on the same project.

    Currently, SharpDevelop does not use the multi-targeting model of VS 2008, but continues to use the model of SharpDevelop 2.x: The “Target Framework” setting in the project options allows you to choose the compiler to work with. If you choose C# 2.0 / .NET 2.0, SharpDevelop will not use ToolsVersion=”3.5” on the project, and will create a solution file version 9. Only if you choose the C# 3.0 compiler, it will make the project an MSBuild 3.5 project and mark the solution as version 10.

    However, this introduces several problems:
    C# 2.0 does not support embedding manifest files in assemblies, but C# 3.5 embeds a default manifest file automatically. Simply changing the target framework changes the manifest behavior.

    MSBuild 2.0 does not check whether the assemblies you referenced are available in the target framework you have chosen. SharpDevelop can fix this by doing the check on its own.

    The target framework is an option that normally can be dependent on the chosen configuration/platform. However the ToolsVersion attribute in MSBuild cannot be dependent on configuration/platform. If one configuration uses C# 3.0, all configurations will; even if they are explicitly set to C# 2.0.

    WPF Applications created in SharpDevelop 2.2 use a different way to support XAML and resource compilation than MSBuild 3.5 uses. Such applications can be edited in SharpDevelop 3.0 and will continue to work even though they are set to C# 2.0 / .NET 2.0 in the options.  Setting these projects to C# 3.0 causes problems that can only be fixed by manually editing the .csproj file.

    So what should we do?

    1. Keep it as it is: works for usual projects, has the problems mentioned above
    2. Do it like VS 2008, drop support for the C# 2.0 compiler and force conversion of the project
    3. Disable the “Target framework” combo box until the user runs a conversion tool for that project (which could be a button next to the target framework combo box)

    1. “Keep it broken” has the advantage that it’s zero work for us
    2. Would be the easy to implement, but means that you cannot use SharpDevelop 3.0 if your co-worker still uses SharpDevelop 2.x or Visual Studio 2005
    3. Would be more work than 2, but is the most flexible solution

    So do you think the ability to edit VS 2005 projects is worth the effort of implementing 3 instead of 2?

    Posted Sep 30 2007, 06:26 PM by Laputa
    Filed under:
  • Embedding manifests

    From .NET 1.x times, you might remember .manifest files as the way to enable XP visual styles support in your application.

    Application manifests are important in Vista. If your application does not have a manifest, Vista will treat it as legacy application and enable file and registry virtualization. This allows the program to write to its own application directory even if it does not have write access to it - the writes are stored in some hidden folder in the user profile. This redirection can cause a lot of confusions when users try to save files using your program where they don't have write access, and instead of getting an error message the files get saved somewhere else!

    Vista also has to determine if programs need administrative rights and should cause a UAC prompt. To do that, it uses a heuristic that looks at the file name and string resources to guess if it should elevate. Using a manifest, you can tell Vista if your program needs elevation and avoid the risk of Vista guessing incorrectly.

    Previously, you had to distribute the .manifest file along with the .exe; or you had to embed it in the .exe using a post-build action calling mt.exe.

    The C# 3.0 compiler has built-in support for embedding manifests, and actually creates a default manifest that turns virtualization off and tells Vista not to elevate if you don't explicitly tell the compiler not to embed a manifest.

    Support for embedding manifests is now available in the SharpDevelop project options:

    You can choose to embed the default manifest, not embed any manifest, or to embed a custom manifest.
    If you select <Create...>, SharpDevelop will automatically create a file called "app.manifest" in your project. The app.manifest created by SharpDevelop is similar to the default manifest the C# compiler uses; it just adds some comments so you don't have to look up how to control Vista's UAC.

    Posted Sep 29 2007, 12:08 AM by Laputa
    Filed under:
  • And now compiling in SharpDevelop is 40% faster

    Yesterday I wrote about how I made compiling 30% faster by using multi-threading to let my dual-core CPU work with both cores. A problem was that I had to use serialize build events to send them back to the host process.

    MSBuild provides these events to loggers (number of occurrences during
    the test shown in parenthesis):
    
    MessageRaised   (21156)
    ErrorRaised      (0)
    WarningRaised   (18)
    BuildStarted      (47)
    BuildFinished     (47)
    ProjectStarted  (449)
    ProjectFinished (449)
    TargetStarted (2910)
    TargetFinished (2910)
    TaskStarted (4308)
    TaskFinished (4308)
    CustomEventRaised (0)

    All these events must be send to the SharpDevelop process, where logger AddIns can process them. Each one is serialized separately, and includes information about the full name of the project file being build and some other highly redundant information. Each event is about 1 KB in serialized form. Half of the events occurred in the worker running in the SharpDevelop process, but the other half occurred in the worker that ran in its own process; so half of the events had to be transferred, so we tranferred a total of 18 MB during the 25-second build. Remember the main cost is the serialization (.NET BinaryFormatter), not the data transfer itself.

    Obviously, most of these events are not interesting for SharpDevelop, and the best way to improve performance is to ignore uninteresting events in the worker instead of ignoring them in the host.
    So which events does SharpDevelop need? Potentially all, since AddIns can add loggers; but if those AddIns tell SharpDevelop which
    events are required before the build starts, we can optimize for the common case that all AddIns are only interested in specific events - usually only in errors and warnings. Events that SharpDevelop currently does not use anywhere are MessageRaised, TargetStarted and TargetFinished - all are called quite frequently.
    To our benchmark results from yesterday, I added an entry "Less events" where MessageRaised, TargetStarted and TargetFinished are deactivated:

    Core Duo Notebook, 1 GB RAM, Vista Home Premium, Aero disabled:
     Rebuild:
       One worker:  35.2, 34.9, 35.2, 35.0, 34.3 : Average 35.1 seconds
       Two workers: 25.6, 23.9, 24.2, 23.5, 23.8 : Average 24.2 seconds
       Less events: 23.7, 23.5, 23.0, 22.4, 21.2 : Average 22.8 seconds

     Build (no changes):
       One worker:  7.4, 7.1, 7.2, 7.8, 7.1 : Average 7.3 seconds
       Two workers: 6.9, 6.8, 6.7, 6.6, 6.2 : Average 6.6 seconds
       Less events: 5.1, 5.0, 6.2, 5.6, 5.1 : Average 5.4 seconds

    More than 1 second saved! (see yesterdays' post for details about the benchmark)

    Now why does SharpDevelop need TaskStarted and TaskFinished? Take a look at the code:
    void OnTaskStarted(object sender, TaskStartedEventArgs e)
    {
      activeTaskName = e.TaskName;
      if (MSBuildEngine.CompileTaskNames.Contains(e.TaskName.ToLowerInvariant())) {
        AppendText("${res:MainWindow.CompilerMessages.CompileVerb} " + Path.GetFileNameWithoutExtension(e.ProjectFile));
      }
    }

    TaskStarted is just there to let the user know that building a project resulted in a recompilation - when doing a Rebuild, SharpDevelop
    shows "Compiling ProjectName" for each project, whereas for a Build (no changes), no such lines are shown in the Output view.
    But in fact, all consumers of TaskStarted probably look for specific task names. And all consumers of TaskFinished are also listening to TaskStarted and are also only interested in some specific types of tasks.
    If we pass the build worker a list of task names we are interested in, it can send us only those events and we don't spend time serializing the tons of events about the tasks <Copy>, <Message> etc.
    Together with the "Less events" changes, this decreases the event count from over 36000 to less than 1000.

    I implemented this in revision 2696.

    Now let's extend our benchmark:
    Core Duo Notebook, 1 GB RAM, Vista Home Premium, Aero disabled, two workers, new event handling:
     Rebuild: 20.3, 20.9, 19.9, 20.2, 20.0 : Average 20.3 seconds
     Build (no changes): 4.3, 4.6, 5.0, 4.4, 4.2 : Average 4.5 seconds

    That's better than "Less events" because we're sending even less events now, but logger AddIns can tell SharpDevelop that they need to receive additional events, so AddIns did not lose any functionality (but if you install such an AddIn, of course you would lose the performance benefits).
    So we're now down from formerly 35.1 seconds (using only one core) to 20.3 seconds - that's 43% faster.
    For "Build (no changes)", the reduction of the event count now makes using two workers efficient; from originally 7.3 seconds to 4.5 seconds is a 39% decrease in build time.

    Posted Sep 27 2007, 03:31 PM by Laputa
    Filed under:
  • Compiling in SharpDevelop just got 30% faster

    [Warning: long post. Short version: Building in SharpDevelop goes multi-threaded, scroll down for benchmark results]

    SharpDevelop 3.0.0.2694 contains a rewritten build system. It has some new features that might be interesting for AddIn developers, but one new feature is directly useful for users, or more specifically, users with multi-core CPUs.

    Here a quick history on build systems in SharpDevelop:
    SharpDevelop 2.0 (the first version to use MSBuild), compiled solutions by telling MSBuild to build the .sln file.
    SharpDevelop 2.1 and 2.2 compile solutions by telling MSBuild to build the projects individually.

    When writing the build engine for SharpDevelop 2.1, I tried to make it multi-threaded so that two projects that are not dependent on each other can be built in parallel. However, this did not work: MSBuild makes use Environment.CurrentDirectory; and if two MSBuild instances run in the same process at the same time, they overwrite the CurrentDirectory and cause the other instance to search files in the wrong location.

    Now I solved this problem by moving the build into worker processes. However, this means that all MSBuild events are serialized and remoted to the host process, where SharpDevelop AddIns need to have access to them - e.g. the CodeAnalysis AddIn registers itself as MSBuild logger, waits for the TaskStarted, ErrorRaised, WarningRaised and TaskFinished events and fixes certain FxCop error messages (some FxCop errors are not associated with a line number, but with a class/member name). Other SharpDevelop AddIns might decide that certain MSBuild events should be displayed in some form, e.g. in the output window. The only way to give AddIns access to all MSBuild events like they had before is to serialize all events and send them to the SharpDevelop process. When doing a build where there's relatively few work but lots of events (e.g. "Clean solution"), the worker is sending up to 1 MB/s of serialized events to the host.

    This had a noticeable impact on performance, so I've kept the first worker in the SharpDevelop process, and only additional workers get their own process. So if you use only one worker, you won't see any worker process and should get the same build performance as SharpDevelop 2.x.

    So even though building a project has multiple parts, of which some are waiting for the disk and others are waiting for the CPU; the overhead of using a worker process will cause the build to get slower. (MSBuild has to check if input files changed, start the compiler; the compiler will load input files, actually compile, write the output assembly; and some other stuff happens in the obj\ directory that you probably don't want to hear about)

    But if you have a dual core processor, the CPU overhead doesn't hurt: a little overhead for one core is better than not using that core at all (this is assuming the compiler you use is not multithreaded).

    But enough talking, let's get some benchmark results:

    I used SharpDevelop 3.0.0.2694 to compile the source code for SharpDevelop 2.2.1:
    AMD 3500+ (single core), 2 GB RAM, Vista Ultimate (32 bit), Areo enabled:
     Rebuild:
       One worker: 34.3, 34.1, 34.6, 34.7, 34.3 : Average 34.4 seconds
       Two workers: 39.6, 38.7, 38.1, 37.0, 36.6 : Average 38.0 seconds

     Build (no changes, so it's not compiling, but just checking everything for changes):
       One worker:  8.5,  8.0,  8.3,  8.0,  8.0 : Average  8.2 seconds
       Two workers: 13.4, 13.9, 14.0, 12.8, 13.1 : Average 13.4 seconds

    The number of MSBuild events (Target started, Task started, Log Message...) is not much different between Rebuild and Build (no changes), so we get a constant overhead of about 5 seconds for the SharpDevelop solution on my desktop computer with a single core processor.

    Here finally is the interesting part: Test results from my dual core notebook:

    Core Duo T2300, 1 GB RAM, Vista Home Premium (32 bit), Aero disabled:
     Rebuild:
       One worker: 35.2, 34.9, 35.2, 35.0, 34.3 : Average 35.1 seconds
       Two workers: 25.6, 23.9, 24.2, 23.5, 23.8 : Average 24.2 seconds

     Build (no changes):
       One worker: 7.4, 7.1, 7.2, 7.8, 7.1 : Average 7.3 seconds
       Two workers: 6.9, 6.8, 6.7, 6.6, 6.2 : Average 6.6 seconds

    For build (no changes), the overhead plays a big role, but using two workers still made the build a little faster.

    But for the real job when there's something to compile, using two workers really rocks: My build is now 30% faster.

    SharpDevelop 3.0 allows you to set the number of workers under Tools > Options > Project and solution > Number of projects to build in parallel. The default value for single-core processors is 1, for other processors it's 2. I don't know if going above 2 will have benefits for quad-core users as the overhead (both CPU for sending events to the host and memory because of the additional processes) increases with the number of workers.

    Please leave a comment here with your test results - I'm looking for feedback from other multi-core users.

    And if you're doing your own benchmark, some notes on how I did my benchmark:
    - make sure enough RAM is free so that not only the system does not swap, but Windows has enough room to keep the files you are compiling in memory (this is generally a good idea if you want your system to be fast)
    - don't count the first test run as the files are not yet in memory there
    - reuse the same SharpDevelop instance for all test runs
    - if you leave less than 60 seconds between test runs, SharpDevelop will re-use the worker process, otherwise it is shut down and has to be restarted. I did my tests re-using a single worker process. It might be interesting to see if the overhead of restarting the worker process is worth keeping it longer than 60 seconds.

    Posted Sep 26 2007, 10:05 PM by Laputa
    Filed under:
Powered by Community Server (Commercial Edition), by Telligent Systems
Don't contact us via this (fleischfalle@alphasierrapapa.com) email address.