Autor |
Beitrag |
AndyB
      
Beiträge: 1173
Erhaltene Danke: 14
RAD Studio XE2
|
Verfasst: Mo 28.08.06 22:12
2cHH hat folgendes geschrieben: | Du meinst, Delphi hat auch eine Möglichkeit, um Speicher zu allozieren
und freizugeben, die mit der von C kombatibel ist? |
Das hängt davon ab, ob du gegen die msvcrtXX.dll (MSVC) / cp3245mt.dll (BDS 2006) linkst. In diesem Fall könntest du die free() Funktion importieren. Ansonsten hast du da wenig Changen (wie auch eine andere C++ DLL keine Change hätte)
_________________ Ist Zeit wirklich Geld?
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Mo 28.08.06 23:57
2cHH hat folgendes geschrieben: |
Die DLL brauche ich ja wohl nicht wie in C mit "Bool FreeLibrary(ModuleHandle)"
wieder freizugeben, ich denke das wird Delphi schon machen, oder? |
Im Prinzip nicht, aber wie neulisch schonmal jemand geschrieben hat: Du räumst dein Zimmer ja auch ab und zu mal auf. Im Idealfall nicht erst, wenn du ausziehst!
Bezogen auf den Fall: was du reservierst/anforderst/sonstwie erhältst, solltest du immer freigeben. Schon weil man vorher nie so genau weiß, ob wirklich alles von Delphi freigegeben wird.
Allerdings hat nicht mal Borland das so genau genommen, in der Graphics.pas ist auch ein Leck. TBitmap gibt irgendwas nicht richtig frei, weiß nicht mehr genau was. Das zeigt einem FastMM bei gesetztem Compilerschalter an. Ist schon interessant zu wissen, das auch Borland Fehler macht
2cHH hat folgendes geschrieben: | So langsam fängt Delphi an, Spass zu machen  |
So ist Delphi nun mal
//Martok
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Di 29.08.06 10:23
Martok hat folgendes geschrieben: | Bezogen auf den Fall: was du reservierst/anforderst/sonstwie erhältst, solltest du immer freigeben. Schon weil man vorher nie so genau weiß, ob wirklich alles von Delphi freigegeben wird. |
Wird es ja, sobald das Programm beendet wird.
Nein, Spass beiseite, ich habe mal etwas nachgefoscht, Delphi scheint die DLL nicht dynamisch nachzuladen, sondern gleich bei Programmstart. Das Nachladen ist wohl auch möglich (wie ich es verstanden habe), dann muss die DLL aber vorher auch explizit geladen werden.
Dynamisches Nachladen ist imho nur sinnvoll, wenn ein Programm länger läuft und dabei mehrere DLLs benutzt, die es aber nicht ständig braucht. Mein Programm braucht die DLL bei jedem Buttonklick und müsste die DLL dann jedesmal neu laden.
Ich habe das Programm auch mal in zwei Instanzen gestartet, die DLL wurde (laut Processexplorer) nur einmal geladen und von "beiden" Programmen benutzt. Genau so soll es meiner Meinung nach auch sein.
Aber bevor ich mich daran mache, die Funktion von C auf Delphi umzuschreiben, wollte ich nochmal sehen, ob ich den Speicher nicht von Delphi aus reservieren kann. So ähnlich wie es schon vorgeschlagen wurde, allerdings will ich erstmal nur den Zeiger auf den Speicher übergeben, keine boolschen Variablen.
Die DLL habe ich schon so umgeschrieben, dass sie als ersten Parameter den Zeiger auf den Puffer erwartet. Der Speicher wird in Delphi mit der Funktion GetMem reserviert, weil eine Initialisierung mit 0, wie sie durch AllocMem vorgenommen wird, nicht nötig wäre:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| var Form1: TForm1; sCont: PChar; pCont: ^Char; nSize: Integer;
implementation
{$R *.dfm}
Function MyReplace(sBuffer: PChar; sSource: PChar; sSearch: PChar; sReplace: PChar): PChar; StdCall; external 'replaceDLL.dll' name 'strReplace';
procedure TForm1.Button1Click(Sender: TObject); begin sCont := PChar(RichEdit1.Text); nSize := StrLen(sCont); GetMem(pCont, nSize); RichEdit1.Text := MyReplace(pCont, sCont, '1', '2'); end; |
Mein Problem ist jetzt, dass ich ^Char nicht als Typ für das Funktionsargument angeben kann (wird scheinbar nicht akzeptiert), aber PChar wohl nicht mit ^Char kompatibel ist.
Wie kann man den Zeiger auf den Puffer übergeben?
ps: die Fehlermeldung ist:
Zitat: | Incompatible types: 'Unit1.Char' and 'System.Char' |
|
|
Motzi
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: Di 29.08.06 11:02
Hi,
ich will an dieser Stelle nur einen kleinen Hinweis auf 2 meiner Tutorials absetzen.  Es handelt sich dabei um Tutorials zu den Themen "Pointer" und "Strings", beide speziell auf Delphi ausgerichtet. Da du sowohl mit dynamisch reserviertem Speicher als auch Strings/PChars und diversen Casts dazwischen arbeitest, könnten dir ein paar Tipps aus den Tutorials vermutlich noch ein bisschen bei der Optimierung weiterhelfen.
Gruß, Motzi
Edit: PChar ist zwar als ^char definiert, aber dennoch sind es für Delphi 2 verschiedene Typen (Delphi hat eben eine sehr strenge Typprüfung)! Du kannst das aber ganz einfach beheben indem du pCont als PChar deklarierst.
_________________ gringo pussy cats - eef i see you i will pull your tail out by eets roots!
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Di 29.08.06 12:05
Hm, mein Fehler. Ich hab nicht gesehen, dass du die DLL statisch einbindest. Ich hab DLLs eigentlich immer dynamisch, schon falls der User eine alte Version der DLL hat, die möglicherweise eine Funktion noch nicht enthält. Dann kann man das Programm wenigstens trotzdem starten. Das geht mit statisch eingebundenen nicht.
Aber wie dem auch sei: versuch doch mal eine Delphi-Portierung deiner Funktion. Ist gar nicht schwierig, sieht hinterher bloß anders aus
//Martok
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Di 29.08.06 14:50
Hi,
ich habe jetzt die Speicherbelegung in Delphi hinbekommen:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
| procedure TForm1.ButtonReplaceClick(Sender: TObject);
var pSource: PChar; pSearch: PChar; pReplace: PChar; pResult: PChar; nSource: Integer; nSearch: Integer; nReplace: Integer; nResult: Integer;
begin pSource := PChar(RichEditSource.Text); pSearch := PChar(RichEditSearch.Text); pReplace := PChar(RichEditReplace.Text); ; nSource := StrLen(pSource); nSearch := StrLen(pSearch); nReplace := StrLen(pReplace); ; if(nSearch > 0) then begin nResult := (nSource div nSearch)* nReplace + nSource mod nSearch; if(nResult < nSource) then nResult := nSource; GetMem(pResult, nResult + 1); MyReplace(pResult, pSource, pSearch, pReplace); RichEditSource.Text := AnsiString(pResult); FreeMem(pResult); end; end; |
Funktioniert 1A mit der DLL. Jetzt habe ich mir überlegt, die gleiche Funktion in Delphi zu realisieren. Das String-Tutorial habe ich auch schon angefangen. Wenn es nachher um das optimieren des Delphi-Code geht, wird es mir bestimmt helfen.
Erstmal habe ich ohne Optimierung einfach versucht, die Funktion 1:1 zu übertragen. Dass dabei nicht das Maximum an Performance entstehen wird, ist klar. Das Übertragen allein ist aber auch ein gute Übung, weil ich dabei von C auf Delphi umdenken muss.
Ein paar Sachen lassen sich leider nicht so einfach wie in C realisieren, imho hat der Programmierer bei C mehr Freiheiten was z.B. Casts angeht. Die Typenstrenge hat bestimmt auch Vorteile, macht mir aber zur Zeit noch Probleme.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
| procedure DelphiStrReplace(sBuff: PChar; sCont: PChar; sSearch: PChar; sRepl: PChar); var nToCopy: Integer; pCont: PChar; pSearch: PChar;
begin if not (sSearch^ = '\0') then begin sBuff := sCont; end else begin nToCopy := 0; while not (sCont^ = '\0') do begin if(sCont^ = sSearch^) then begin pCont := sCont; pSearch := sSearch; while(((++pCont)^ = (++pSearch)^) and (pCont^) and (pSearch^)); ; if(pSearch^ = '\0') then begin strncat(sBuff, (sCont - nToCopy), nToCopy); nToCopy := 0; strcat(sBuff, sRepl); sCont += nSearch; continue; end; end; sCont++; nToCopy++; end; strncat(sBuff, (sCont - nToCopy), nToCopy); end; end; |
Eine Stringkonstante 'abc', ein Character 'a'. Soweit ich es verstanden habe. Problem ist jetzt aber der NullChar, der den String abschliesst '\0' sollte nicht als String, sondern als Char interpretiert werden.
Bin ich überhaupt auf dem richtigen Weg, lässt sich das ganze so wie in C realisieren?
Muss ich das continue z.B. durch ein Flag ersetzen?
Gruss aus Hamburg
|
|
mkinzler
      
Beiträge: 4106
Erhaltene Danke: 13
Delphi 2010 Pro; Delphi.Prism 2011 pro
|
Verfasst: Di 29.08.06 15:01
#0 statt '\0';
Inkrement per ++pCont geht auch nicht, selbiges gilt für sCont += nSearch;
du kannst PChar direkt durch Addieren weitercshieben
Delphi-Quelltext 1: 2: 3:
| p := PChar( 'ABC'); inc(p); => p := 'BC'; |
_________________ Markus Kinzler.
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Di 29.08.06 20:57
mkinzler hat folgendes geschrieben: | #0 statt '\0';
Delphi-Quelltext |
thnx, ich hab's jetzt in Delphi hinbekommen:
*freu*
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45:
| procedure DelphiStrReplace(sBuff: PChar; sCont: PChar; sSearch: PChar; sRepl: PChar);
var pCont: PChar; pSearch: PChar; nToCopy: Integer; nSearch: Integer;
begin if (sSearch^ = #0) then begin sBuff := sCont; end else begin sBuff^ := #0; nToCopy := 0; nSearch := StrLen(sSearch); while not (sCont^ = #0) do begin if(sCont^ = sSearch^) then begin pCont := sCont; pSearch := sSearch; while( (pCont^ = pSearch^) and (not (pCont^ = #0)) and (not (pSearch^ = #0)) ) do begin inc(pCont); inc(pSearch); end;
if(pSearch^ = #0) then begin StrLCat(sBuff, (sCont - nToCopy), (StrLen(sBuff) + nToCopy)); nToCopy := 0; StrCat(sBuff, sRepl); sCont := sCont + nSearch; continue; end; end; inc(sCont); inc(nToCopy); end; StrLCat(sBuff, (sCont - nToCopy), (StrLen(sBuff) + nToCopy)); end; end; |
So sieht die direkte Portierung der Funktion aus der DLL aus. Leider erreicht sie nicht die Performance der DLL. Dass die Funktion in C etwas schneller ist, kann man wohl als normal betrachten, immerhin ist die DLL in reinem C geschrieben und auch so kompiliert, das ist imho (weil einfacher gestrickt) auch noch mal etwas schneller als C++.
Trotzdem glaube ich, dass aus der Implemantation in Delphi noch mehr rauszuholen ist, so dass sie noch dichter an die Funktion in C herannkommen kann. Das NonPlusUltra wäre meiner Meinung nach, durch Assemblerbefehle immer gleich 4 Chars auf einmal zu laden, aber so weit wollte ich bei meinem ersten Delphiprogramm nicht gehen.
Ich könnte mir vorstellen, dass StrCat und StrLCat zu langsam sind. Hinzu kommt noch, dass alle Längen vor der Aktion bekannt sind. StrCat und StrLCat müssen imho erst das NullChar finden, dann das NullChar entfernen, den Inhalt dazukopieren und dann das Nullchar wieder anhängen. Das tut hier eigentlich doch nicht Not, weil die Position vom NullChar mitgeführt werden kann.
Wenn ich nachdem gehe, was ich im Stringtutorial bis jetzt gelesen habe, was zugegeben noch nicht viel ist, könnte man auch einen Char-Array deklarieren.
Mal sehen, was am besten funktioniert.....
Gruss aus Hamburg
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Mi 30.08.06 00:54
Ich habe jetzt die StrCat-Funktionen rasugeworfen und durch etwas schnelleres ersetzt:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| procedure MyStringPaste(nBuff: Integer; sBuff: PChar; sToPaste: PChar; nToPaste: Integer);
var nIndex: Integer; nCount: Integer; nLastPos: Integer;
begin nCount := 0; nLastPos := nBuff + nToPaste - 1; for nIndex := nBuff to nLastPos do begin sBuff[nIndex] := sToPaste[nCount]; inc(nCount); end; sBuff[nIndex ] := #0;
end; |
nBuff ist der OffSet im Puffer, von dem an geschrieben werden soll und wird von der Replace-Funktion als erster Parameter zusätzlich übergeben.
Das hat ganz schön was gebracht.
Hätte ich nicht erwartet, wie schnell das jetzt schon läuft.
Damit ich das mal alles vergleichen kann, wie stelt man den Delphi-Compiler so ein, das er den schnellsten Code produziert?
Es gilt 16 MB Textersetzung (jedes zweite Zeichen) in ca. 3 sec zu schlagen.
(mehr konnte ich die Version mit DLL nicht verbessern)
Gruss aus Hamburg
|
|
|