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