CT- Vergleichstest Programmiersprachen



  • Hallo,
    ich wurde in den letzten Tagen immer wieder nach meinem Vergleichsprogrammm zum damaligen CT- Artikel (Arne Schäpers, C++ deutlich hinter C#, Java und Delphi, insbesondere der BCB nochmal deutlich langsamer als Visual C++) befragt, und wie meine Ergebnisse waren. Nun habe ich mein Programm damals aus dem Frust "hingeschmiert", bin mir also nicht sicher, ob es wirklich inhaltlich das gleiche macht, oder ob ich eine Kleinigkeit übersehen (oder vielleicht richtig einen Bug eingebaut) habe. Ich habe nicht nur die Parameterübergaben per Referenz eingebaut, sondern auch die komplette VCL rausgeworfen. Falls jemand den Artikel gelesen hat und sich das Originalprogramm aus dem Internet geladen hat, ich würde mich über Statements freuen. Wie gesagt, Basis war das Beispielprogramm aus der CT, ich habe es nur angepaßt und wollte damit auch keinen Schönheitspreis gewinnen. Einige der Funktionen stehen auch etwas im Leeren. Sicherlich kann man das Programm, sollte es so inhaltlich richtig sein, weiter optimieren.

    #ifndef SingleEntryH
    #define SingleEntryH
    
    #include <string>
    #include <map>
    using namespace std;
    
    class TSingleEntry;
    
    typedef map<string, TSingleEntry*, less<string> >  MapEntryType;
    typedef MapEntryType::iterator                     MapEntryTypeIterator;
    typedef MapEntryType::const_iterator               MapEntryTypeCIterator;
    typedef MapEntryType::value_type                   MapEntryTypeValue;
    
    class TSingleEntry {
       protected:
          string FEntryName;
          TSingleEntry* ParentList;
       public:
          TSingleEntry(void);
          TSingleEntry(string const& EName);
          TSingleEntry(string const& EName, TSingleEntry* Parent);
       public:
          string const&         EntryName(void) const { return FEntryName; }
          string                FullName(void) const;
          virtual string        GetDisplayName(void) const;
          virtual TSingleEntry* FindEntry(string const& PartialName);
          virtual TSingleEntry* AddEntry(string const& EName);
          virtual TSingleEntry* AddNode(string const& NName);
    
          virtual bool begin(MapEntryTypeIterator&) { return false; };
          virtual bool next(MapEntryTypeIterator&) { return false; };
       };
    
    class TListEntry : public TSingleEntry {
      private:
         MapEntryType         EntryList;
         MapEntryTypeIterator aktIter;
      public:
        TListEntry(void);
        TListEntry(string const& EName);
        TListEntry(string const& EName, TSingleEntry* Parent);
        virtual ~TListEntry(void);
    
        virtual string        GetDisplayName(void) const;
        virtual TSingleEntry* FindEntry(string const& PartialName);
        virtual TSingleEntry* AddEntry(string const& EName);
        virtual TSingleEntry* AddNode(string const& NName);
    
        bool begin(MapEntryTypeIterator&);
        bool next(MapEntryTypeIterator&);
    
    };
    #endif
    

    Datei SingleEntry.h

    // SingleEntry.cpp : Implementierungsdatei
    //
    
    #include <string>
    #include <map>
    using namespace std;
    
    #include "SingleEntry.h"
    // TSingleEntry
    
    TSingleEntry::TSingleEntry()
    {
    	ParentList = NULL;
    }
    
    TSingleEntry::TSingleEntry(string const& EName)
      : FEntryName(EName), ParentList(NULL)
    {
    }
    
    TSingleEntry::TSingleEntry(string  const& EName, TSingleEntry* Parent)
     : FEntryName(EName), ParentList(Parent)
    {
    }
    
    string TSingleEntry::FullName() const {
    
      if (ParentList != NULL) return ParentList->FullName()+'\\'+ EntryName();
      else return EntryName();
    }
    
    string TSingleEntry::GetDisplayName(void) const {
      return FEntryName;
    }
    
    TSingleEntry* TSingleEntry::AddEntry(string const& /*EName*/) {
      return NULL;
    }
    
    TSingleEntry* TSingleEntry::AddNode(string const& NName) {
      return AddEntry(NName);
    }
    
    TSingleEntry* TSingleEntry::FindEntry(string const& PartialName) {
      if (FEntryName == PartialName)
    		return this;
    	else
    		return NULL;
    }
    
    // BListEntry
    
    TListEntry::TListEntry(void) {
    }
    
    TListEntry::TListEntry(string const& EName) : TSingleEntry(EName) {
    
    }
    
    TListEntry::TListEntry(string const& EName, TSingleEntry* Parent)
              : TSingleEntry(EName, Parent) {
    
    }
    
    // In C++ ein Destruktor nötig, der die Listen durchläuft
    TListEntry::~TListEntry(void) {
      for(MapEntryTypeIterator iter = EntryList.begin();
          iter != EntryList.end(); iter++) {
          TSingleEntry *entry = iter->second;
          delete entry;
      }
      EntryList.erase(EntryList.begin(), EntryList.end());
    }
    
    TSingleEntry* TListEntry::FindEntry(string const& PartialName) {
      TSingleEntry* Result = TSingleEntry::FindEntry(PartialName);
      if(Result == NULL) {
        MapEntryTypeCIterator iter = EntryList.begin();
        while(iter != EntryList.end()) {
           Result = (iter->second)->FindEntry(PartialName);
           if(Result != NULL) break;
           iter++;
          }
      }
      return Result;
    }
    
    TSingleEntry* TListEntry::AddEntry(string const& EName) {
      TSingleEntry* Result;
      EntryList.insert(MapEntryTypeValue(EName, Result = new TSingleEntry(EName, this)));
      return Result;
    }
    
    TSingleEntry* TListEntry::AddNode(string const& NName) {
      TListEntry* Result;
      EntryList.insert(MapEntryTypeValue(NName, Result = new TListEntry(NName, this)));
      return Result;
    }
    
    string TListEntry::GetDisplayName() const {
      return TSingleEntry::GetDisplayName() + " (LIST)";
    }
    
    bool TListEntry::begin(MapEntryTypeIterator& iter) {
      iter = EntryList.begin();
      return iter != EntryList.end() ? true : false;
    }
    
    bool TListEntry::next(MapEntryTypeIterator& iter) {
      iter++;
      return iter != EntryList.end() ? true : false;
    }
    

    Datei SingleEntry.cpp

    Das Hauptprogramm stelle ich als Antwort hinter diesen Artikel, da ich nicht weiss, ob ich sonst nicht irgendwelche Beschränkungen erreiche. Danke schon jetzt an alle, die mal über das Programm sehen.

    Schöne Grüße aus Berlin

    Volker



  • Habe leider erst jetzt gesehen, dass es für C++ Quelltexte einen eigenen Tag gibt, so sieht es doch schon viel freundlicher aus. Da mich insbesondere der Delphi- Vergleich gestört hat und ich damals die RoadShow vorbereitet habe, habe ich nicht nur die STL statt VCL- Containern eingesetzt, sondern auch die VCL durch wxWindows ersetzt. Also gleich ein Beispiel für wxWindows.

    //---------------------------------------------------------------------------
    // ListFrame
    //---------------------------------------------------------------------------
    #ifndef ListFrame_H
    #define ListFrame_H
    
    #include "SingleEntry.h"
    
    #include <string>
    using namespace std;
    
    class ListFrame : public wxFrame {
      public:
         ListFrame(string const& title);
    
         virtual ~ListFrame(void);
      protected:
    
         // Hilfsfunktionen
         void ShowEntry(string const& s);
         void PrintListList(TSingleEntry* list, int level);
         void BuildListList(TSingleEntry* list, int level);
         void ClearList(void);
         void BuildBaseList(int iCount);
    
         string FindListEntry(TListEntry* list, string const& substr);
         string FindEntry(TListEntry* list, string const& substr);
    
         string DWordTime(DWORD millisecs);
    
         void OnCreateTreeClick(wxCommandEvent& event);
         void OnBenchCreateClick(wxCommandEvent& event);
    
         wxButton*  button1;
         wxButton*  button2;
    
         wxListBox* listBox;
    
         DECLARE_EVENT_TABLE()
    
      private:
         TListEntry* RootDir;
         int         EntryCount;
    };
    
    #endif // ListFrame
    

    Datei: ListFrame.h

    //---------------------------------------------------------------------------
    // ListFrame.cpp
    //---------------------------------------------------------------------------
    #include <wx/wx.h>
    #include <mmsystem.h>
    
    #include <stdlib.h>
    
    #include <string>
    using namespace std;
    
    #include "ListFrame.h"
    
    BEGIN_EVENT_TABLE(ListFrame, wxFrame)
       EVT_BUTTON(10000, ListFrame::OnCreateTreeClick)
       EVT_BUTTON(10001, ListFrame::OnBenchCreateClick)
    END_EVENT_TABLE()
    
    ListFrame::ListFrame(string const& title) :
       wxFrame(NULL, -1, title.c_str(), wxDefaultPosition, wxSize(248, 443)) {
    
       button1 = new wxButton(this, 10000, "bCreateTree (100 Entries)", wxPoint(24,10),
                              wxSize(193, 25));
    
       button2 = new wxButton(this, 10001, "bBenchCreate (1 Mio Calls)", wxPoint(24,45),
                           wxSize(193, 25));
    
       listBox = new wxListBox(this, 10100, wxPoint(10,80),wxSize(217, 297));
    
       RootDir = NULL;
       randomize();
       }
    
    ListFrame::~ListFrame(void) {
      if(RootDir) delete RootDir;
    }
    
    // Hilfsmethoden
    
    void ListFrame::ShowEntry(string const& s) {
      listBox->Append(s.c_str());
      return;
    }
    
    void ListFrame::PrintListList(TSingleEntry* list, int level) {
        string Lead = "";
        TSingleEntry* E;
        int x;
    
        for (x = 1; x < level; x++) Lead += "  ";
        // EntryName+' (LIST) für Verzeichnisse
        ShowEntry(Lead + list->GetDisplayName());
    
        // Dateieinträge und Verzeichnisse, ungeordnet
        MapEntryTypeIterator iter;
        if(list->begin(iter)) {
           while (list->next(iter)) {
              E = iter->second;
              PrintListList(E, level+1);
           }
        }
      return;
    }
    void ListFrame::BuildListList(TSingleEntry* list, int level) {
      int LocalCount, NameLength;
      string NewName;
    
      // maximal 25 Einträge pro Liste - nur das Stammverzeichnis
        // kann mehr haben
      LocalCount = rand() % 25  + 1;
    
      for (int x = 1; x <= LocalCount; x++) {
         EntryCount--;
         // zufälliger Name mit 1-8 Zeichen
         NameLength = rand() % 8 + 1;
         NewName = "";
         for (int y = 1; y <= NameLength; y++) NewName += char((int)'a' + (rand() % 26));
    
         // Einzelner Eintrag oder neue Liste? Die Wahrscheinlichkeit
         // für neue Listen (und weitere Rekursion) sinkt mit zunehmender
         // Verschachelungstiefe
         if (rand() % (10 * level) < 1)
              BuildListList(list->AddNode(NewName), level + 1); // new list
         else list->AddEntry(NewName);
    
         if (EntryCount == 0) break;
        }
      return;
    }
    
    void ListFrame::ClearList(void) {
      if (RootDir) delete RootDir;
      RootDir = NULL;
    }
    
    void ListFrame::BuildBaseList(int iCount) {
      if (RootDir == NULL) RootDir = new TListEntry("C:");
      EntryCount = iCount;
    
      // irgendein Startwert für rand()
      srand((unsigned)time(NULL));
    
      while (EntryCount > 0) BuildListList(RootDir, 1);
    }
    
    string ListFrame::FindListEntry(TListEntry* list, string const& substr) {
      TSingleEntry* Result = list->FindEntry(substr);
    
      if (Result != NULL) return Result->FullName();
      else return "";
    }
    
    string ListFrame::DWordTime(DWORD millisecs) {
      DWORD std, min, sec, ms;
      std = millisecs / 3600000;
      min = millisecs % 3600000 / 60000;
      sec = millisecs % 60000 / 1000;
      ms = millisecs % 1000;
      char szBuffer[30];
      sprintf(szBuffer, "%.2d:%.2d:%.2d.%.3d", std, min, sec, ms);
    
      return szBuffer;
    
    }
    
    // Ereignismethoden
    
    void ListFrame::OnCreateTreeClick(wxCommandEvent& event) {
      if(!listBox->IsEmpty()) listBox->Clear();
      ClearList();
      BuildBaseList(100);
      PrintListList(RootDir, 1);
      ClearList();
      return;
    }
    
    void ListFrame::OnBenchCreateClick(wxCommandEvent& event) {
    
       listBox->Clear();
       listBox->Update();
       DWORD fullBench;
       DWORD constructionTime = 0;
       DWORD destructionTime = 0;
       DWORD findTime = 0;
       DWORD meanTime;
    
       ClearList();
       fullBench = timeGetTime();
       for (int x = 1; x <= 10; x++) {
          meanTime = timeGetTime();
          BuildBaseList(100000);             // Konstruktion
          constructionTime += (timeGetTime() - meanTime);
          meanTime = timeGetTime();
    
         // Suchen
         ShowEntry("Search a: " + FindListEntry(RootDir, "a"));
         ShowEntry("Search ax: " + FindListEntry(RootDir, "ax"));
         ShowEntry("Search axv: " + FindListEntry(RootDir, "axv"));
         ShowEntry("Search axve: " + FindListEntry(RootDir, "axve"));
    
         for (int y = 1; y <= 20; y++)
            FindListEntry(RootDir,"X"); // gibts nicht
    
         findTime += (timeGetTime() - meanTime);
         listBox->Update();
    
         meanTime = timeGetTime();
         ClearList();                       // Destruktion
         destructionTime += (timeGetTime() - meanTime);
       }
    
       string Msg;
       Msg += "Time:\t\t" + DWordTime(timeGetTime()-fullBench);
       Msg += "\nfor Construction:\t" + DWordTime(constructionTime);
       Msg += "\nfor Destruction:\t" + DWordTime(destructionTime);
       Msg += "\nfor Find:\t\t" + DWordTime(findTime);
       wxMessageBox(Msg.c_str());
    
       return;
    }
    

    Datei: ListFrame.cpp



  • zum Schluß die Applikation

    //---------------------------------------------------------------------------
    // MainApp.h
    //---------------------------------------------------------------------------
    #ifndef __MAIN_APP_H
    #define __MAIN_APP_H
    
    class ListOOPApp : public wxApp {
       public:
          virtual bool OnInit(void);
    };
    
    DECLARE_APP(ListOOPApp)
    
    #endif // __MAIN_APP_H
    

    Datei: MainApp.h

    //---------------------------------------------------------------------------
    // MainApp.cpp
    //---------------------------------------------------------------------------
    
    #include <wx/wx.h>
    
    #include "mainApp.h"
    #include "ListFrame.h"
    
    bool ListOOPApp::OnInit(void) {
       wxFrame* frame = new ListFrame("wxWindows List OOP");
       frame->CreateStatusBar();
       frame->SetStatusText("ListList with wxWindows");
       frame->Show(TRUE);
       SetTopWindow(frame);
       return true;
    }
    
    IMPLEMENT_APP(ListOOPApp)
    

    Datei: MainApp.cpp

    Für das Originalprogramm mit dem BCB6 ergaben sich auf meinem Rechner die folgenden Zeiten, die annährend mit der CT übereinstimmen.

    Time:              00:00:23.778
    for Construction:  00:00:12.181
    for Destruction:   00:00:00.126
    for Find:          00:00:11.471
    

    Für den CBX mit wxWindows (soll keine Wertung für wxWindows oder CBX sein, der verwendete Compiler beim CBX ist der gleiche, wie beim BCB6)

    Time:              00:00:07.406
    for Construction:  00:00:02.516
    for Destruction:   00:00:00.344
    for Find:          00:00:04.546
    

    Wie gesagt, die Unterschiede sind doch dramatisch, und es ist nur schnell gestrickt. Deshalb traue ich dem Braten auch noch nicht so ganz. Wenn ich etwas übersehen haben sollte, bitte posten.

    Die STL gehört zu C++, es wurde also keine Geheimbibliothek verwendet. Wenn mein Programm also inhaltlich richtig arbeitet, dann wären die Verhältnisse wieder geklärt, und es würde sich zeigen, dass der Compiler vom BCB6 richtig eingesetzt, auch schnell ist (gerade der wurde von der CT ja am heftigsten kritisiert).

    Schöne Grüße aus Berlin

    Volker



  • noch mal zur Erinnerung, hat jemand das gelesen und es sich angesehen? hat jemand einen Tipp, ob ich doch etwas übersehen habe?



  • Hallo,
    schade, mich hätte wirklich interessiert, ob jemand sich das angesehen hat, und etwas dazu sagt. Die Ergebnisse sind einfach zu gut. Auf der anderen Seite ist es C++ ja auch.

    Es ist immer einfach zu meckern, vielleicht fällt Euch auf, wie lang der Diskussionspfaden zum C++BuilderX ist, und wie leicht dort pauschal geurteilt wurde (ich möchte keinen direkt angreifen). Dieser Vergleichstest hat nun mal für Schaden und Aufreckung gesorgt, und wenn man sich nicht mit vernünftigen Gegenbeispielen und Argumenten ausstattet, muss man sich nicht wundern, wenn dann andere Sprachen den Zuschlag geben und die Unterschiede immer mehr verwischen.

    Schöne Grüße aus Berlin

    Volker


Anmelden zum Antworten