IDE Fix Pack 4.5 developer snapshot

By | October 18, 2011

I’ve put IDE Fix Pack 4.5 back into development as it wasn’t as stable as I wanted it to be. But this also allows me to put new optimizations into the code and improve the performance of older optimizations. Furthermore I added support for RAD Studio XE2+Update 1.

What has changed and what is new

First I made the unit name hash more stable. It sometimes doesn’t get rebuild after a unit is unloaded as the compiler seems to have multiple ways of doing the unload (or I haven’t found the right place to hook). So it looked for access violations when accessing the hash table to detect if a unit was removed from memory. Now it is notified by a hook in the compiler’s memory manager about releasing the memory of a unit.

The directory cache got some tweaks and sanity checks can be enabled by setting the environment variable IDEFixPack.ValidateDirCache=1. It allows you (or me) to identify bugs in the cache algorithm. But it will drastically slow down the compilation speed, especially in XE2 with its unit scope lookups.

The Compile Progress dialog slows down the compiler because both run in the same thread and GUI updates are slow. With this IDE Fix Pack the update time of the dialog is reduced to 80 milliseconds or a file name change. This allows to compiler to work on the code instead of waiting for the GUI to update. And then there is this optimization in the IDE’s code that only updates the dialog if the file name or the current line has changed. This would be a good optimization if there wouldn’t be RAD Studio XE and XE2 which load all units into memory when you “make” the project. And during this unit loading the compiler calls the progress dialog with the project’s file name and the last line in the project. So it is called thousands of time but no CPU time is spent on calling ProcessMessages due to this optimization what makes the dialog hang and you can’t cancel the compilation during that phase. I have changed that so you can press the Cancel button. The linker phase is still unresponsive as the compiler doesn’t call into the IDE during the linking phase.

Embarcadero (finally) introduced caches into the compiler. They have the UnitFindByAlias, FileSystem, SearchUnitNameInNS and GetUnitOf caches (http://blog.barrkel.com/2011/10/delphi-xe2-compiler-performance.html). IDE Fix Pack already had the UnitFindByAlias cache (=> unit name hash), FileSystem cache (=> directory cache) and SearchUnitNameInNS (=> part of the directory cache). What IDE Fix Pack didn’t have was the GetUnitOf cache. So I hooked into that function and profiled it with and without the cache. With my largest test project (4M+ LOC) the GetUnitOf function was called 52,625 times and took 16.36 seconds. After disabling the cache it needed 16.24 seconds. The problem with the cache is that the function is called in only 2% with the same parameter. But even without the cache 16 seconds is too slow for 52,625 calls. So I looked for a different algorithm and found one that utilizes a binary search instead of multiple linear searches. The result is that it now takes less than 300 milliseconds. “Unfortunately” GetUnitOf seems to be called not that often in normal units. Only my auto-generated unit (8 MB, 130,000 lines) and the MSOffice TLB import units caused the the compiler to call it that often.

The FileSystem cache is another cache that Embarcadero introduced. But it is no match for my directory cache. Especially not because it caches the same directory multiple times if it is accessed from different relative paths. My directory cache on the other hand uses the fastest way to get the directory listings (FindFirstFileEx under Windows 7+ with FindExInfoBasic and FIND_FIRST_EX_LARGE_FETCH). It also “pre-calculates” where DCU/DCP and PAS files are so it doesn’t have to ask every directory listing hash table, It gets the full and relative file name with one hash table lookup. My cache also keeps a list of “not found” files what helps to speed up the search for include files. So I have disabled the compiler’s FileSystem cache.

I don’t have much to say about the compiler’s UnitFindByAlias cache except that I have also disabled it. But not because it would be slow. The reason is that my other compiler optimizations depend on that information so I use my unit name hash instead of the compiler’s cache.

The SearchUnitNameInNS cache is the only cache that I have left active because my implementation doesn’t work. It isn’t cleared at the right time and returns outdated information. For example: The compiler calls the function with “Classes” and on the first call it returns “Classes”. So my patch puts “Classes”=>”Classes” into its hash table. But when the function is called the second time with “Classes” the original function returns “System.Classes” whereas my cache would return “Classes” from the hash table lookup.

Name IDE Version File Size Downloads Added
DelphiSpeedUp 3.1-untested RC for Delphi 6 6 DelphiSpeedUpV31D6.7z 74.59 KB 1220 times 2011-04-22
DelphiSpeedUp 3.1-untested RC for Delphi 2006 2006 DelphiSpeedUpV31D2006.7z 106.03 KB 1309 times 2011-04-22
DDevExtensions 2.4.2 7/2007 speed 7, 2007 DDevExtensions242Setup7_2007.zip 535.43 KB 3549 times 2011-08-02

fastdcc

All the above changes are also in the fastdcc.exe command line compiler patcher.

There are 2 ways to use fastdcc.exe.

1. Using fastdcc.exe directly

  1. Extract the 7z file into your $(BDS)\bin directory.
  2. start fastdcc.exe as if it was dcc32.exe.

1. Replacing dcc32.exe by fastdcc.exe

  1. Extract the 7z file into your $(BDS)\bin directory.
  2. Rename dcc32.exe, dcc32.jdbg and dcc32.de/fr/jp to dcc32compiler.exe/jdbg/de/fr/jp.
  3. Rename fastdcc.exe to dcc32.exe
  4. Rename fastdccHook.dll to dcc32Hook.dll
  5. start dcc32.exe or msbuild.

 

Why are there no compiler optimizations for the 64 bit and MacOS compiler?

I haven’t had a look at them (from a “patch point of view”). And I don’t know if I will invest time in them in the XE2 time frame as it would mean that I have to maintain 6 compilers and 4 IDEs. Guess why I have discontinued Delphi 7/2007 when XE2 was released. Delphi 2009 support will be there for a long time as I actively use it at work.

C++ Builder XE/XE2 Patch

I almost forgot that IDE Fix Pack for XE and XE2 contain another experimental patch for C++Builder XE/XE2. I have replaced the “String hash table” that the C++ compiler uses for all strings. In XE if you create a new C++ VCL Forms Application and press Ctrl+Space in the editor, the C++ compiler calls the “findStrEntry” (what I called it) 521,001 times with 76,641 unique strings. The original implementation uses a very bad hash function. It has a hash table with max. 32,768 buckets but findStrEntry puts the 76,641 unique strings in only 4,416 buckets. The largest number of strings in a bucket is 74. And 2,295 buckets have more than 9 strings in the linear list. Only 724 strings have a bucket for themselves.
I have rewritten the findStrEntry function with a much better hash function. And it now uses 28,750 buckets. The largest number of strings a bucket is 20. Only 302 strings have a linear list with more than 9 strings and 7,928 strings have a bucket for themselves.

Let’s sum it up

findStrEntry calls 521,001
unique strings 76,641
max. buckets 32,768
original: buckets used 4,416
original: max. number of strings in a bucket 74
original: buckets with more than 9 strings 2,295
original: buckets with 1 string 724
new: buckets used 28,750
new: max. number of strings in a bucket 20
new: buckets with more than 9 strings 302
new: buckets with 1 string 7,928

25 thoughts on “IDE Fix Pack 4.5 developer snapshot

  1. Eric

    Great work.

    I’m wondering why Embarcadero didn’t just contact & contract you before working on those caches themselves, if only to get broad directions on what and how to cache.

    On a more general note, I wonder if they’re using profilers themselves or know how to properly leverage profilers, as this isn’t the first time I’ve seen them go to great extents to introduce optimizations and complex code that ends up sub-optimal or just plain non-beneficial (already reported a few, and I’m finding more while play-testing FMX).

    1. Andreas Hausladen Post author

      You have to compare their caches to the XE compiler without IDE Fix Pack. And then their caches give you a performance boost. But the boost by IDE Fix Pack is higher (on my test projects)

      1. Eric

        Yes, that’s my point. They invested time and came up with an inferior solution, while they could have just asked for some insight.

        I’m quite sure that had they asked you, the compiler would now be faster and simpler than both what they achieved or what you could achieve now through IDEFixPack (if only by ensuring the presence of clean hook points).

  2. Heinrich

    Just a quick feedback: after installing version 4.5 dev 1 for Delphi 2009 I get lot’s of “[Fatal Error] F2084 Internal Error: BR1640” under a certain condition.

    My setting:
    Delphi starts, my previous project is re-opened automatically. Code is shown in the editor. If I now hover over a function call when the Structure view is still empty, then instead of context information I get the Internal Error (multiple times). If I instead click first inside the editor window (which leads to the Structure view being filled) and _then_ hover over the same function it says “Parsing…” for some time and then shows the context help. This is reproducible.

      1. Heinrich

        Just tried, 4.4 works fine.

        Behavior in V4.4:
        – Case 1: no click in editor, just hovering => about 15 seconds happens nothing, then context info shows up
        – Case 2: click in editor, then hovering => “Parsing…” shows up as context hint for about 15 seconds, then context info shows up

        Behavior in V4.5:
        – Case 1: no click in editor, just hovering => about 3 seconds happens nothing, then Messages view shows 27 times the [Fatal Error], short delay after the first appears, then the rest shows up at once
        – Case 2: click in editor, then hovering => “Parsing…” shows up as context hint for about 15 seconds, then context info shows up

        My project group is fairly large, containing many packages.

        1. Andreas Hausladen Post author

          I can’t reproduce this.
          You could try to disable some IDE Fix Pack patches to nail it down.
          SET IDEFixPack.DisabledPatches=Compiler.GetUnitOf
          bds.exe

          SET IDEFixPack.DisabledPatches=Compiler.FileSearchCache;Compiler.DirectorySearch
          bds.exe

          1. Heinrich

            Well it seems gone with dev2 and I don’t have dev1 around anymore to double check. But I also closed and opened some units in the meantime. Who knows, everything is fine now.

            Thanks for your efforts and for a Delphi starting up lightning fast!

  3. Albert

    Hello,
    Once again a big thank you for this piece of gold and your effords

  4. Eike Petersen

    Nice work – as usual. Embarcadero really should pay you for your efforts – without your tools & patches their compilers would be much less usable to me. Thanks!

  5. Peter

    There is no difference in using dcc32 and fastdcc (150kloc, 7 seconds). Am I doing soemthing wrong? Maybe my project is to small?!

    1. Andreas Hausladen Post author

      First of all: I never wrote that fastdcc makes the compiler that much faster. I only wrote that it contains the same compiler optimizations that IDE Fix Pack contains.

      For XE and older: If you have a fast hard disk the directory cache won’t help you that much. Then the GetUnitOf function isn’t called that often for “normal” units (as I wrote above). And the Kibitzing optimization (CodeCompletion, ErrorInsight paring) isn’t used on the command line. But those “7 seconds” that the compiler tells you do not include the clean up time that can take seconds and that fastdcc made a lot faster what also helped CodeCompletion to pop up much faster as it doesn’t need 4 seconds to clean up the compiler’s internal data from the last compilation.

      For XE2: XE2 already has a directory cache. And the GetUnitOf function isn’t called that often for “normal” units…

  6. Tommaso Ercole

    Sorry but in this version there is still problem looking up for include files.

    It should be noticed that when there is a relative path delphi compiler tries to look for it in every directory of the library path.

    So if there is ..\ in front of the file it should look it in every parent folder of each directory in library path.

    For me it is still a no.

    P.S. It would be helpful a section where it is explained how u can disable each feature.

    1. Andreas Hausladen Post author

      I haven’t changed the directory cache algorithm because I wasn’t able to reproduce this include bug. But I have added an environment variable, that helps to find that bug. You could define the env-var “IDEFixPack.ValidateDirCache=1” and then run the IDE. This should open a console window the moment where the original IDE code and my directory cache return different values.

      > P.S. It would be helpful a section where it is
      > explained how u can disable each feature

      If you open the Help/About dialog in the IDE and select “IDE Fix Pack” in the “Installed products” list, you can copy the content of the “Productinfomation” memo into an editor and you’ll see all possible strings for the “IDEFixPack.DisabledPatches” environment variable.

  7. Tommaso Ercole

    I just enabled that option but when the compiler stops the last voices are completely different from the error shown.

    That console windows, in my specific case, is full of lines… more than the console buffer.

    Is there a log file where i can take that log or must i use output redirection?

    1. Andreas Hausladen Post author

      > Is there a log file where i can take that log or must i use output redirection?

      I didn’t thought that it would be that much. If you want to help me to find this bug I would made some logging changes to IDE Fix Pack and would upload that special version here.

        1. Tommaso Ercole

          I used it but there are two things:
          1) If I compile, now it crashes immediately without finding system.dcu
          2) If I build, at the end of the build process it complains saying:
          [DCC Error] E2161 Error: RLINK32: Error opening file “.Res”. The res file is changing according to the moment.

          1. Andreas Hausladen Post author

            Now that is interesting. The logging-version just calls the original GetTimeStamp function and then logs its input and output parameters/result. There shouldn’t be any problem with missing files.
            Was the log file created? Can you send it to Andreas.Hausladen@gmx.de ?

          2. Andreas Hausladen Post author

            Are you using the “background compiler” ?

            … And I found the bug. It only affects the background compiler and the ErrorInsight parser. Instead of a “not equal” I wrote “equal” what caused the compiler to accept every file path as an existing path.

Comments are closed.