CMapStringTo… HashKey Implementierung in VC-2003/5/8 ist auch fragwürdig

 Hash-Algorithmen haben es mir irgendwie angetan. 1984 hatte ich das erste mal mit einem miesen Hashverfahren zu tun, das einfach versagte wenn es nur um Ziffern ging. Das ganze betraf das Betriebssystem OASIS (später TheOS) und dessen Index Dateiorganisation. Ich entwickelte hierfür einen Patch in Assembler, der damals exakt in den x-Bytes der Kernels in Z80 Assembler passen musste.
Das Problem bei Strings die nur aus Ziffern bestehen ist das die Veränderung, die immer nur die unteren 4 Bits betrifft und die höheren 4 Bits immer konstant sind mit 3 belegt sind.

Als ich gelesen habe,  dass in VS-2010 die CMap Implementierung nun auch für Strings den selben Algorithmus aus der STL erhalten, habe ich mir auch den mal genauer angesehen. (siehe Beitrag Die MFC erhält mit VC-2010 jetzt eine neue HashKey Implementierung)
Der sieht so aus:

inline UINT CMapStringToString::HashKey(LPCTSTR key) const
{
  UINT nHash = 0;
  if (key == NULL)
  {
    AfxThrowInvalidArgException();
  }
 
  while (*key)
    nHash = (nHash<<5) + nHash + *key++;
  return nHash;
}

Und auch in diesem Fall muss ich sagen, dass der Algorithmus nur scheinbar intelligent ist. Wenn man sich aber die Abstände die hier erzeugt werden etwas genauer anschaut kommt man schnell auf eine Wertekombination, in der der Algorithmus versagt.
Wie sehr er versagt zeigt das folgende Beispiel, in dem ich einfach einen String verwende der aus 5 Ziffern besteht und immer den Abstand 11 verwendet.

CMapStringToPtr myMap;
for (int i=0; i<1000; i+=11)
{
  CString strKey;
  strKey.Format(_T("%05d"),i);
  myMap[strKey] = NULL;
}

Das erschreckende Ergebnis der Verteilung ist bei einem Unicode wie auch MBCS Projekt:

TMyMap<class CMapStringToPtr>::DumpMap
Bucket  0 =  0
Bucket  1 =  0
Bucket  2 =  0
Bucket  3 =  0
Bucket  4 =  0
Bucket  5 =  0
Bucket  6 =  0
Bucket  7 =  0
Bucket  8 = 36
Bucket  9 =  0
Bucket 10 =  0
Bucket 11 =  0
Bucket 12 =  0
Bucket 13 =  0
Bucket 14 = 55
Bucket 15 =  0
Bucket 16 =  0

Wie gut, dass auch die String Variante von HashKey überarbeitet wird.

BTW: Da die internen Strukturen protected sind muss man zu einem kleinen Trick greifen um die Verteilung ausgeben zu können. Ich habe dazu ein template verwendet, dass für alle MFC Maps funktioniert.

template<class TBase> class TMyMap : public TBase
{
public:
  void DumpMap()
  {
    TRACE(__FUNCTION__ "\n");
    if (m_pHashTable)
    {
      for (size_t i=0; i<m_nHashTableSize; ++i)
      {
        int iCount = 0;
        for (CAssoc *p = m_pHashTable[i]; p; p=p->pNext)
          ++iCount;
        TRACE("Bucket %2d = %4d\n", i, iCount);
      }
    }
  }
};

Merke: Wer Hashes verwendet sollte sich über sein Hashverfahren wirklich gedanken machen.

BTW: Sollte es den Leser meines Blogs langsam langweilen weil ich mich permanent mit Hash-Algorithmen aufhalte, der sei getröstet: Dies war vorläufig mein letzter Beitrag zu CMap… :mrgreen:

Die MFC erhält mit VC-2010 jetzt eine neue HashKey Implementierung

Ich hatte die schlechte Hashkey Implementierung ja bereits in VC-2005 als Bug gemeldet (siehe Die HashKey Implementierung in der MFC in VC-2005 und VC-2008). In VC-2008 geschah dies bzgl. wieder nichts. Aber was lange währt wird manchmal gut 😉

Die neue Implementierung wird aus der aktuellen STL übernommen. Diese Implementierung sorgt für Integer und Pointer für eine sehr gute zufällige Verteilung. Und weitere gute Nachricht dazu, auch die Implementierung für Strings wird aus der STL übernommen.

Zitat:

Hello Martin,

Thanks for the report. This issue has been fixed in MFC for Visual Studio 2010. MFC now uses the STL hash functions directly when possible, or uses a new algorithm copied from STL (in the case of strings).

Pat Brenner
Visual C++ Libraries Development
Von Microsoft am 20.07.2009 um 12:15 bereitgestellt

Siehe https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=468860

Auslösung: DrawText unter Vista gegenüber XP um bis zu Faktor 50 langsamer!

Am 17. Januar habe ich den folgenden Artikel geschrieben: DrawText unter Vista gegenüber XP um bis zu Faktor 50 langsamer!

Ich möchte Euch die Auflösung des Problems nicht vorenthalten.

Eigentlich ist es keine Lösung sondern nur der Fakt, dass auch XP unter gleichen Bedingungen genauso lahm ist wie Vista.
Es liegt an den erweiterten Spracheinstellungen, die unter XP optional sind aber eben nicht mehr unter Vista. Dort sind die immer mit installiert.

So sieht das ganze bei einer normalen XP Installation aus, mit der entsprechenden Performance:
XP-DrawText-Fast

Man kann sehen, dass die zwei unteren Checkboxen aus sind. Wenn man diese nun einschaltet und die entsprechenden Module nachinstalliert werden, dann erlebt man unter XP nach einem Neustart die selben Geschwindigkeitseinbruch wie unter Vista:
XP-DrawText-Slow

Mein Testprogramm läuft fast 50mal langsamer als bei der Standardinstallation und damit genauso schnell/lahm wie unter Vista. Nimmt man die zwei Checks wieder heraus, dann hat man den alten gewohnten Speed.

Wenn man unter Vista in den entsprechenden Dialog der Systemeinstellung sieht, kann man auch sehen, dass man hier nichts mehr beschleunigen kann durch eine eventuelle Deinstallation, denn offensichtlich gehören diese Bestandteile bei Vista zum Inventar:
Vista-DrawText-Slow

So und damit ist auch diese Supportanfrage bei Microsoft „ungelöst“, aber zumindest „erklärt“ geschlossen.

Ich frage mich dennoch warum eine solche EInstellung solche Auswirkungen haben muss. Letzten Endes sind das auch nur Fonts mit denen umgegangen werden muss. Ich finde diesen extremen Unterschied auffällig, allerdings wird sich vermutlich nichts daran ändern…

Ich wünsche allen Lesern einen schönen Juli und verziehe mich jetzt erstmal für die nächsten 2 1/2 Wochen ohne Laptop und PC an die Nordsee, zum Radfahren, Baden und Drachen steigen lassen… 😉

Die HashKey Implementierung in der MFC in VC-2005 und VC-2008

Die Maps in der MFC werden ja auch zum Teil gerne verwendet. Auch wenn viele Programmierer eher auf die std::map bzw. std::hash_map verwenden.

Zwei interne Dinge sind jedoch vielen Entwicklern nicht klar.

  1. Die Standard-Maps der MFC werden mit 17 Hash-Buckets erzeugt. Man sollte sich also über die Anzahl der Elemente klar werden, die gespeichert werden sollen.
    Bei großen Datenmengen sollte man evtl. darüber nachdenken, die Anzahl der Buckets zu erhöhen.
  2. Ist die Hash Funktion zu beachten, die hier verwendet wird. Denn deren Effektivität entscheidet ja, wie die Einträge auf die Buckets verteilt werden.

Zu diesem zweiten Punkt macht man eine erstaunliche Entdeckung, wenn man sich ansieht, was Microsoft für eine Funktion vorgesehen hat um den Hask-Key zu erzeugen. Diese Funktion ist in der MFC für primitive Werte vordefiniert, als primitive Werte sehe ich alle numerischen Werte, Zeiger und Handle an.
Man findet eine Implementierung als Template-Funktion in afxtempl.h:

template<class ARG_KEY>
AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key)
{
 // default identity hash - works for most primitive values
 return (DWORD)(((DWORD_PTR)key)>>4);
}

Das erstaunliche ist für mich, dass hier die untersten 4 Bits einfach abgeschnitten werden. Für mich hätte für Handles, Zeiger und Integer Werte einfach ein cast gelangt.

Nehmen wir jetzt mal an, wir haben eine einfache Datenmenge, die die Integer 1-100 auf eine Struktur abbilden. Man wird die erstaunliche Entdeckung machen, dass alle Werte nur in den Buckets 0-6 abgespeichert werden. Die Buckets 7 bis 16 werden nicht verwendet.

Der Nachteil ist offensichtlich. Man nutzt das Datenrauschen auf den untersten 4 Bits nicht.
Das macht sich sogar bei Fensterhandles bemerkbar. Bekanntlich sind das immer gerade Werte, aber auch auf den Bits 1-3 finden wir hier Informationen. Auch hier werden nützliche „zufällige“ Informationen nicht genutzt. Dadurch werden mehr Kollisionen in Kauf genommen als notwendig wären.

Ich möchte nicht unerwähnt lassen, dass die MFC Maps wirklich eine Berechtigung haben, weil sie grundsätzlich einen Pool-Allocator verwenden. Häufige Allokationen und Löschungen werden weitaus schneller behandelt, als durch die std::map, oder std::hash_map mit normalen Allocator.

Ich habe diesen Bug (oder miese Verhalten) bereits in der Beta für VC-2008 gemeldet. Es wurde aber nicht mehr geändert. Nun habe ich es an die Produktgruppe erneut  für VC-10 eingereicht.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100772
Nachtrag 22.06.2009: Da der Bug von Microsoft nicht neu geöffnet wird habe ich einen neuen für VS-2010 Beta 1 angelegt.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=468860
Wer Lust hat, der kann ja abstimmen.

BTW: Die HaskKey Funktion in afxtempl.h ist für andere Maps (z.B. CMapPtrToPtr) direkt in der Klasse definiert. Dieses Template wird nur vom CMap-Template verwendet.

Refactoring mit Hilfe des Compilers kann eine tückische Sache werden

Wieder mal eine nette Falle: Implizite Konvertierungen und ein Refactoring-Versuch.

Folgende Methoden wurden in einer Klasse verwendet:

...
bool GetTableCoreData(long lIdAddrSet,
            CAgvipTableCoreData &coreData,
            bool bSilent=false);
bool GetTableCoreData(long lIdAddrSet, long lIdProject,
            CAgvipTableCoreData &coreData,
            bool bSilent=false);
bool GetTableCoreData(long lIdAddrSet,
            CDataConnection &dataConnection,
            CAgvipTableCoreData &coreData);
...

Die dritte Methode passte mir nicht von der Reihenfolge der Argumente. und ich änderte sie wie folgt um:

bool GetTableCoreData(long lIdAddrSet,
            CAgvipTableCoreData &coreData,
            CDataConnection &dataConnection);

Ich habe mich nun einfach darauf verlassen, dass der Compiler mir alle entsprechenden Code Stellen schon anmeckern wird, an denen hier was nicht passt. Da ich noch einiges anderes an der Klasse geändert hatte, dauerte es noch eine Weile bis ich den nächsten Build angeworfen habe, und ehrlich gesagt, habe ich das Refactoring dieser Funktion vergessen.
Typischer Fall von: Zu viel auf einmal & Der Compiler macht einfach nicht was ich will 😉

Was passierte? Nichts ❗
Ich bekam keine Fehlermeldung zu dieser Änderung, denn CDataConnection hat eine implizite Konvertierung auf bool. Die Folge war, dass die erste Signatur der Funktion auch dieser Folge von Argumenten entsprach.

bool GetTableCoreData(long lIdAddrSet,
            CAgvipTableCoreData &coreData,
            bool bSilent=false);

Logisch, dass diese Funktion natürlich eine anderes Verhalten hatte und hier nicht mehr das passierte was ich eigentlich wollte.
Dämlicherweise rutschte diese Änderung auch noch durch die Tests und eine ganze Funktionsgruppe unserer Software wurde lahmgelegt und so ausgeliefert… Ein Bug, dazu noch von der Kategorie vermeidbar.
Was lernen wir:

  1. Es gibt keine fehlerfreie Software!
  2. Die kleinen Änderungen bringen die größten Fehler!
  3. Sich beim Refactoring auf den Compiler zu verlassen kann tückisch werden!

Manche Änderungen in den UIs machen einen wahnsinnig, heute das MS-SQL Management Server Studio

Ich nutze viel das MS-SQL Server Management Studio für MS-SQL 2005. Besonders um eben auch SQL-Statements zu testen und zu entwerfen.

Häufig lasse ich mir dazu einfach eine aktuelle Tabelle anzeigen. Mit Strg+3 kann man sich nun das SQL-Statement anzeigen lassen und es ändern. Also z.B. eine WHERE Bedingung hinzufügen.
Jeder der den Enterprise Manager gewohnt ist haut nun auf die F5-Taste, es passiert natürlich nichts 😮 . OK times are changing, wir benutzen also brav Strg+R um das neue Statement auszuführen.

OK nun klicken wir auf den Datenbanknamen und wählen hier im Kontextmenü Neue Abfrage aus. Ein bisschen SQL Statement schreiben und wir drücken Strg+R . 😮 Was denn nun? Wir bemühen also das entsprechende Menü und finden das hier der entsprechende Hotkey F5 ist.

Wer nun wieder mit MS-SQL Server 2008 arbeitet wird nun mit Strg+R kein Glück haben. Jetzt ist alles wieder gut und hier funktioniert nur noch F5. Jetzt ist dafür der Abfrage-Designer ist nun ein separater Dialog geworden, der das entsprechende Statement in das Abragefenster überträgt.
(Nachtrag 2009-05-06) Wenn man aus im Management Studio 2008 den Punkt Zeilen bearbeiten auswählt, hat man das selbe miese Verhalten wie im Management Stusio 2005. Man kommt nur nicht mehr so offensichtlich in diesen Bearbeitungsmodus und landet eher auf einer Abfrage, in der man dann aber das Ergebnis nicht bearbeiten kann.

Es gibt Momente in denen ich mich beherrschen muss nicht in die Tischplatte zu beißen… 😡

LVM_GETSUBITEMRECT mit LVIR_ICON liefert andere Ergebnisse unter Vista als unter XP

Das damit auch die Funktion CListCtrl::GetSubItemRect aus der MFC betroffen ist, ist dann auch  klar.
Manche Sachen ärgern einen einfach. Vor allem wenn man nichts am Code ändert und doch falsches Verhalten erntet.

Wieder mal ist die Vista UI eigentümlich ungereimt, in diesem Fall bei einem List View.

Folgendes ist gegeben:

  • Ein List View (SysListView32) in einem Dialog oder anderen Fenster
  • Der List View hat den Stil LVS_REPORT
  • Der List View hat hat mehr als eine Spalte.
  • Dem List View wurde eine Imagelist zugewiesen.

Führt man nun auf Windows XP LVM_GETSUBITEMRECT /CListCtrl::GetSubItemRect mit LVIR_ICON aus, dann erhält man immer ein Rectangle zurück mit der entsprechenden Weite der Imagelist Symbole. Das Verhalten ist:

  • vollkommen unabhängig ob ein Manifest für COMCTL32.DLL Version 6.0 vorhanden ist oder nicht
  •  es ist auch unabhängig ob LVS_EX_SUBITEMIMAGES gesetzt ist oder nicht.

Macht man das ganze unter Vista, dann liefert LVM_GETSUBITEMRECT /CListCtrl::GetSubbItemRect ein RECT / CRect mit der Weite der Symbole immer dann wenn:

  • kein Manifest für COMCTL32.DLL Version 6.0 vorhanden ist
  • oder LVS_EX_SUBITEMIMAGES gesetzt ist

Das heißt in dem Fall

  • ein Manifest für COMCTL32.DLL Version 6.0 ist
  • und LVS_EX_SUBITEMIMAGES ist nicht gesetzt .

erhält  man ein Rectangle mit der Weite 0 (Null) 😕

Anmerkung:
 Man kann sich natürlich streiten was nun richtig ist. Wenn LVS_EX_SUBITEMIMAGES nicht gesetzt ist, dann macht LVIR_ICON zugegebenermaßen wenig Sinn. Aber es leuchtet irgendwie nicht ein, dass ohne Manifest und ohne LVS_EX_SUBITEMIMAGES, wieder ein Wert zurückgeliefert wird. Entweder ist die Weite von LVS_EX_SUBITEMIMAGES abhängig oder eben nicht.
Das Ganze ist in jedem Falle mal ungereimt und nicht kompatibel ❗

Nachtrag 26.03.2009:
Das List-Control liefert für das Subitem 0 immer ein korrektes Rectangle für LVIR_ICON! Nur wenn wirklich ein Subitem (>0) abgefragt wird, tritt das Problem auf.

Selbst reingelegt beim Test von „XP oder später“

Manche Codezeilen schreibt man ja einfach im Schlaf so in etwa wie diesen hier

static const OSVERSIONINFO &GetOSVersionInfo()
{
 static OSVERSIONINFO osVersionInfo;
 if (osVersionInfo.dwOSVersionInfoSize==0)
 {
  osVersionInfo.dwOSVersionInfoSize = sizeof(osVersionInfo);
  ::GetVersionEx(&osVersionInfo);
 }
 // return pointer to struct
 return osVersionInfo;
}

bool OSIsWinXP()
{
 // Check if OS is XP or later
 const OSVERSIONINFO &osvi= GetOSVersionInfo();
 return (osvi.dwPlatformId & VER_PLATFORM_WIN32_NT)!=0 &&
     osvi.dwMajorVersion>=5 &&
     osvi.dwMinorVersion>=1;
}

Der Sinn und Zweck ist eindeutig. Ich benötige diese Funktion um zu Testen ob Windows XP oder ein späteres OS wie Vista oder Windows 7 installiert ist. Dumm nur das dieser Code dämlich falsch ist.
Die Betriebssysteme haben die folgenden internen Versionsnummern
5.0 – Windows 2000
5.1 – Windows XP
5.2 – Windows Server 2003
6.0 – Windows Vista
6.1 – Windows 7 (Anmerkung: idotisch, dass hier nicht 7.0 verwendet wird)

Als ich den Code schrieb war Windows XP gerade draußen und selbst Windows Server 2003 gerade am Horizont. Dämlicherweise schrieb ich in dem Test osvi.dwMajorVersion>=5 && osvi.dwMinorVersion>=1.
ohne natürlich daran zu denken, dass ein späteres OS wieder mit einer 0 als minor Version kommen könnte.
Dadurch ergibt sich natürlich das der Test für alle Betriebssysteme nach Windows XP funktioniert nur nicht für Windows Vista  weil eben die Minor Version hier 0 ist. Ich war drauf und dran mal wieder einen Bug einzureichen bis mir schlagartig klar war, dass nicht Vista einen Fehler hat sondern mein eigener Code.

Der korrekte Test muss natürlich so lauten:

bool OSIsWinXP()
{
 // Check if OS is XP or later
 const OSVERSIONINFO &osvi= GetOSVersionInfo();
 return (osvi.dwPlatformId & VER_PLATFORM_WIN32_NT)!=0 &&
   (osvi.dwMajorVersion>5 ||    // May be vista or later
       (osvi.dwMajorVersion==5 &&    // that's XP
        osvi.dwMinorVersion>=1));
}

Code wie im Schlaf zu schreiben bringt es manchmal eben nicht. 😉

Slow DrawText performance in Vista and Windows 7. Please comment…

Sorry for blogging in English. I know that I am breaking my own rules 😉

Reasons for this article:
In the last years I found it more an more complicated to get bugs fixed in current software produced by Microsoft. My experience showed me, that the Microsoft product teams always look for new versions and new features and the Microsoft support teams and engineers don’t produce enough pressure that well known bugs get fixed in the currently used software products.

The reason why the Microsoft support rejected a request for a fix was very often that not enough customers are affected by the problem or bug. And this is often a problem for me and my company. We are just a small ISV so we can’t point to millions of affected users. And yes I know that hotfixes are expensive.
So usually we have to be flexible and have to find work arrounds.
But my feeling is that there would be enough impact and pressure for a hotfix, if it would be known to the public what problems exist and that there are others who have a request to get a special problem fixed.
Also a lot of programmers tend to avoid the creation of a support request, because the experience  shows: You put energy, time (and money) into a support request and the answers are in most cases: Will not get fixed, by design, and so on…

Another experience in the last years was, that even being an MVP doesn’t help to get anything fixed by Microsoft even if a lot of MVPs point out that there is a real need for a change or fix. 

So I just want to choose a new way, because there is no way to get votes of other people to a standard bug in a Microsoft product like it is possible on Microsoft Connect. But Connect is absolutely worthless if the product is already shipped!
I want to collect votes for this problem via my blog. I also informed the Microsoft support engineer who cares about the current incident.

If you agree and if you see a larger problem in this bug too, or if you have the feeling or knowledge that it has impact on your own software, than please write a comment to this blog article. Please leave your real email address or homepage URL. The email address will not be visible to the public, and I will forward it only to Microsoft if the poster agrees.

Description of the bug:

On January 17th, 2009 I reported a reproducible bug in Windows Vista in the article DrawText unter Vista gegenüber XP um bis zu Faktor 50 langsamer!  (The article is written in German, you find a description in English below in the next lines)

Fact:
Windows Vista and Window 7 are up to 50 times slower in the execution of a simple DrawText function with DT_CALCRECT set compared to Windows XP.
You can find the source code for a sample code (VS-2005 MFC project) that shows the bug here:  TestDrawText.zip. If you are just interested in the executable you can find it here TestDrawTextExe.zip.

And as far as I can see the overall performance of DrawText is affected.

My request:
If you find it important that this need a fix, or if you have the feeling or the knowledge, that you software might be effected. Please leave a comment here ❗

Or feel free to vote on the bug I filed for Windows 7 on Microsoft Connect:
https://connect.microsoft.com/windows7/feedback/ViewFeedback.aspx?FeedbackID=401007

Thank you

Final Note (11.07.2009):
Read on here
http://blog.m-ri.de/index.php/2009/07/11/ausloesung-drawtext-unter-vista-gegenueber-xp-um-bis-zu-faktor-50-langsamer/

DrawText unter Vista gegenüber XP um bis zu Faktor 50 langsamer!

❗ Ja Ihr habt ganz richtig gelesen und ich irre mich nicht ❗

Wir haben in einem unserer C++/MFC Programmen eine komplexe Anzeige von Reports, die alle unterschiedliche Zeilenhöhe haben und in einem speziellen Fenster angezeigt werden (eigene Entwicklung).
Nun stellten wir fest, dass das Rollen in diesem Programmteil auf Vista-Rechnern mit viel Nachlauf funktionierte und auch einige andere Operationen länger als gewohnt dauerten. Bei meinem Suchen, Profilen und Testen kam ich auf ein sehr lahmes Abarbeiten der Funktion DrawText  mit DT_CALCRECT.

Um das Problem zu isolieren schrieb ich ein kleines Testprogramm, das erstaunliches zu Tage brachte.
Hier die Ergebnisse von 4 verschiedenen Rechnern (Durchlauf von 100×100 DrawText Calls):

  • Pentium Quad-Core Q8200 mit Vista: 48.111 msec
  • Pentium Core2Duo T2310 mit Vista: 78.953 msec
  • Pentium Dual Core 3,2Ghz mit XP: 1.516 msec
  • Alter Pentium 3,0Ghz HT mit XP: 1.922 msec

😯 Der Code ist teilweise um den Faktor 50 langsamer auf Windows-Vista im Vergleich zu Windows-XP

Wer Lust hat es selbst zu testen, der findet hier das Beispielprogramm im Sourcecode (VS-2005):  TestDrawText.zip
Wer das Programm als Exe möchte findet es hier: TestDrawTextExe.zip

Beschreibung:
Dieses Beispielprogramm führt gemäß einem angegebenen Zähler je 100mal DrawText mit DT_CALCRECT für einen längeren mehrzeiligen Text aus. Hierbei werden immer zufällige Textlängen verwendet. Der Algorithmus liefert jedoch immer die gleiche Zufallssequenz, damit der Test auch Vergleichbar bleibt.
Der Zufallsmechanismus wurde nur eingebaut, um für für meinen Fall realitätsnahe Daten zu liefern.

Anmerkung:
Eine entsprechende Supportanfrage bei Microsoft läuft ❗
Was dabei herauskommt werde ich berichten…

Zuletzt angemerkt:
Windows 7 soll ja schneller sein als Windows Vista. Wer weiß? Ich habe es noch nicht getestet.
Vieleicht haben die nur diesen Bug gefixed und einen gigantischen Performanceschub erhalten :mrgreen: … SCNR…

Nachtrag (11.07.2009): 
Die Auiflösung zu diesem Problem findet sich hier in diesem Artikel 
http://blog.m-ri.de/index.php/2009/07/11/ausloesung-drawtext-unter-vista-gegenueber-xp-um-bis-zu-faktor-50-langsamer/