|
Antwort |
Registriert seit: 12. Aug 2004 49 Beiträge |
#71
hi Hagen
hab mal ein paar fragen
Delphi-Quellcode:
beides ist eine restberechnung
// division with remainder, Q = A div B & R = A rem B
procedure NDivRem(var Q,R: IInteger; const A,B: IInteger); overload; // division with modular remainder, Q = A div B, R = A mod B procedure NDivMod(var Q,R: IInteger; const A,B: IInteger); overload; wo ist nun der genaue unterschied zwischen MOD und REM ?
Delphi-Quellcode:
was ist MOD2K?
// modular squaring, A = A^2 mod 2^k, A = B^2 mod 2^k
procedure NSqrMod2k(var A: IInteger; const B: IInteger; K: Cardinal); overload; wie muß man sich das vorstellen? ich hoffe du kannst ein bischen licht in mein dunkel mit dem ganzen "modularen" bringen^^ und was ist "montgomery domain" ??? mfg Dano |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#72
Zitat:
wo ist nun der genaue unterschied zwischen MOD und REM ?
Zitat:
// modular squaring, A = A^2 mod 2^k, A = B^2 mod 2^k
procedure NSqrMod2k(var A: IInteger; const B: IInteger; K: Cardinal); overload; was ist MOD2K? wie muß man sich das vorstellen? -> A = A^2 mod 2^k Vorstellen kannst du es dir so: -> x = 1234^2 mod 2^16 == 1234^2 mod $10000; Solche Operationen sind enorm schnelle Divisionen auf Binärrechnern, logisch da eine Division zu 2^k = ein Exponent zu Basis 2, eben nichts anders wie eine UND Verknüpfung mit 2^k -1 darstellen. Nach A^2 wird also das Resultat auf k Bits abgeschnitten, da man ja mit einer Binärzahl modular dividiert die aus k-1 ein Einsen besteht. Zb. A mod 2^1024 == A and (2^k-1) == A and 0b111111111.111111. Der Divisor besteht also als Binärzahl aus k-1 Einsen. Technisch gesehen bedeutet dies aber nur das man jeden Dividenden einfach auch k Bits in der Größe anbschneidet. Intern im DECMath wird zu jedem IInteger die Größe der verwendeten 32 Bit Werte verwaltet. D.h. die IInteger bestehen aus einem Linearen Speicherbereich der die Zahl enthält und einem Zähler wieviele Cardinal == 32Bit Werte man benötigt um diese große Zahl zu speichern. Eine MOD 2^k Operation wird also im Grunde immer nur diesen Zähler verkleinern und maximal den höchsten 32 Bit Wert im Speicher AND verknüpfen. Der Performancetechnisch Aufwand der MOD 2^k Operationen beschränkt sich demnach immer nur auf eine 32Bit breite AND Operation, auf bei Zahlen und einem k von vielen tausenden Bits. Will man Zb. 1 Million Nachkommastellen von Pi berechnen, benötigt davon aber nur die letzten 8 Hexadezimalen Ziffern, dann kann man das so machen das nach jeder Operation mit MOD 2^32 -> k = 32, modular verkürzt. Du kannst dir nun vorstellen das es ein gewaltiger Unterschied in der Performance ist ob man nur mit 32 Bit großen Werten oder mit 125.000 Bit großen Zahlen rechnet. Man berechnet also nur Teilziffern der gesuchten Zahl und kann so effizient und sehr schnell seine Ergebnisse mit zB. Testdaten aus dem Internet vergleichen. Man erhöht nun k, ausgehend von 32 so das nach jeder richtigen Teilberechnung die nächste Berechnung mit k * 2 durchführt. So kann man sehr effizient Überprüfungen durchführen. In NCombi.pas findest du viele solcher Verfahren die intern modulare Divisionen mit 2^k durchführen können. Zb. möchtest du 2*3*5*7*13.....65537 mod 2^32 berechnen, also das Primorial = Produkt aller Primzahlen zwischen 0 bis 65538 modulo 2^32, also nur die letzten 32 Bits = 8 Hexadezimalen Ziffern. Du benutzt dazu NPrimorial(A, 0, 65538, NInt("HEX:100000000")); d.h. der Modulare Divisor ist 2^32. Intern ruft nun NPrimorial nach jedem Rechenschritt NMod(x, 2^32) auf, und NMod() wiederum kann überprüfen ob der Divisor eine echte Potenz von 2 ist, d.h. NMod() berechnet wenn möglich das k == 32. Falls das zutrifft wird nicht mehr mit einer langsammen Standarddivision gerechnet sondern eben einfach mit Beschneidung des Resultates auf k == 32 Bits. Solche Feinheiten werden erst dann relevant wenn man Zb. bestehende Algorithmen in/aus anderen Libraries nach/von DECMath umsetzt, und sich dann bei Berechnungen mit mod 2^k Zahlen wundert warum DECmath um so vieles schneller als andere Libraries ist. Dies liegt eben daran das DECMath intern so weit dies effizient möglich ist bei vielen Operationen das schnellste Verfahren zur Lösung der Aufgabe ermittelt. Gerade die häufigsten Operationen wie die modulare Exponentation, modulare Multiplikation, modulare Quadrierung usw. profitieren dann davon wenn man mit mod 2^k berechnet.
Zitat:
was ist "montgomery domain" ???
Für eine genaue Beschreibung solltest du besser im Internet danach suchen. Interessant an den meisten Montgomery Tricks ist aber der Fakt das diese Tricks zu einer Zeit entdeckt wurden als man mit Langzahlen wie sie heute durch Computer möglich gemacht werden, damals noch garnicht gab. D.h. Montgomery hat viele Tricks gefunden die zu seiner Zeit keinerlei reale Bedeutungen hatten. Erst viel viel später besann man sich auf diese schon bekannten Tricks zurück um damit verschiedenen Computerberechnungen zu beschleunigen. Fazit: Montgomery hat Wissen entdeckt nur weil es entdeckbar war, und nicht weil man es zur Lösung eines konkreten Zieles nötig war Oder anders ausgedrückt: man entdeckt irgendwas und erfindet erst danach eine konkrete Anwendung dafür. Gruß Hagen |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#73
Zu den Montgomery Funktionen zählen NMont(), NRedc() und NPowMod(). Die Montgomery Domain wird normalerweise in NPowMod() automatisch ausgewählt falls das zu einem Geschwindigkeitsvorteil auf dem jeweiligen Rechnertyp führt. D.h. in den meisten Fällen ist die interne Anwendung des Montgomery TRicks für dich als Anwender vollständig transparent, du bekommst davon nichts weiter mit. Es gibt aber sehr häufig die Notwendigkeit noch mehr spezielle Algorithmen mit DECMath zu entwickeln, die eben sehr viele modulare Divisionen zum gleichen Divisor durchführen. Da aber beim Montgomery Trick die Umwandlung der Zahlen in und aus der Montgomerydomain sehr rechenaufwändig sind, ist es vorteilhafter vor und nach solchen Spezialberchnungen nur einmalig diese Konvertierungen durchzuführen. Nun, dazu wurden eben die Funktionen NMont() und NRedc() verfügbar gemacht. Eine Anwendung dieses Falles sind die Funktionen zur Berechnng in Elliptischen Kurven in der Unit NGFps.pas.
Ich hätte diese Funktionen auch nur intern verwenden und deklarieren können, aber warum solche Funktionalität verstecken wenn man sie später eventuell gebrauchen kann ? Fazit: diese Funktionen sind sehr sehr spezieller Natur, können aber wenn man weis wie und wo die Laufzeit ganz bestimmter Algorithmen stark positiv beeinflussen. Sie sind also nur Support-Funktionen. Gruß Hagen |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#74
Hi Dano,
ich habe mich mal des NRoot() Problemes angenommen. Du hast natürlich Recht gehabt, NRoot() berechnet unter bestimmten Umständen den Rest falsch. Hier mal der korregierte Source für NRoot() aus meiner Lib.
Delphi-Quellcode:
Gruß hagen
function NRoot(var A,R: IInteger; const B: IInteger; E: Integer): Boolean;
// A = B^(1/E), R = B - B^(1/E)^E, returns R == 0 if B is a perfect power of E // NRoot(1MBit, 3) = 31.6 seconds // TBigNum.NRoot(1MBit, 3) = 44.9 seconds // @A, @R can be nil, if both are nil we can check for PerfectPower // todo: apply a recursive karatsuba newton method, equal to NSqrt() resourcestring sNRoot = 'NRoot(), B must be > 0 and E > 0'; var X,Y,T: IInteger; I: Integer; begin Result := True; if E <= 0 then NRaise(@sNRoot); I := NSgn(B, True); if I <= 0 then NRaise(@sNRoot); if (E = 1) or (I = 1) then begin if @A <> nil then NSet(A, B); if @R <> nil then NSet(R, 0); end else if E = 2 then Result := NSqrt(A, R, B) else begin NBit(Y, (NSize(B) + E -1) div E, True); Dec(E); repeat NSet(X, Y); NPow(T, Y, E); NMul(Y, T, E); NAdd(T, Y); // now here -> T = Y^E * (E +1) NMul(Y, X); NAdd(Y, B); NDivRem(Y, T, Y, T); I := NCmp(Y, X); until I >= 0; // HR, BugFIX!, we have to recompute on some circumstances the remainder // Thanks to Dano to pointing me out this. if I <> 0 then begin NPow(T, X, E +1); NSub(T, B, T); end; Result := NSgn(T) = 0; if @A <> nil then NSwp(A, X); if @R <> nil then NSwp(R, T); end; end; |
Zitat |
Registriert seit: 12. Aug 2004 49 Beiträge |
#75
hi Hagen
erstmal danke für deine sehr ausführliche erklärung von Mod/Mod2k/Montgomery ich werde mir noch ein paar quellen im internet suchen um das zu vertiefen, aber im großen und ganzen habe ich das erstmal verstanden dann danke für den bugfix von NRoot.... nun muß ich mir nur was einfallen lassen wie ich das am besten einbaue... wenn ich es in eine extra unit stecke und die dann bei uses importiere, nimmt er doch immer wieder die alte NRoot aus NInts.dcu ich habe mich jetzt ein wehnig mit Interfaces beschäftigt... die sind eigentlich eine feine sache durch das referenzcounting. nur bei IInteger komme ich auf keinen grünen zweig. normalerweise müßte das interface mit einer klasse verbunden sein, aber so richtig fündig bin ich nicht geworden. oder kann man interfaces auch ohne klasse verwenden? da sie aber nur methoden haben dürfen kann ich mir das nicht vorstellen was mich nun verzweifeln läßt ist das ich als variable nur ein interface deklariere und keine klasse oder struct. also wie verwaltet das interface dann seine daten wenn es keine felder hat? leider sind die deutschen quellen im internet nicht sehr ergibig wie das interface intern so richtig arbeitet muß ich doch mal auf dem englichen sector suchen mfg Dano |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#76
Hi Dano,
das sind sehr gute Fragen. Es ist richtig das IInteger KEINE Objecte sind aber denoch Interfaces. Es sind preallozierte Records bzw. genauer gesagt stinknormale Interfaces Der Irrglaube heutzutage ist eben das Interfaces aus Objecten bestehen müssen, falsch: die Objecte sind eine "umständlicher" Überbau auf die Interfaces. D.h. die einfachste Interface Implementierung sind Records. Bisher kenne ich aber immer noch keine andere Delphi Source die dieses Tricks,wie bei IInteger, benutzen oder kennen. Es gibt nun mehrere Gründe warum IInteger so arbeitet: 1.) das Interface von DECMath ist komplett prozedural -> sprich es macht keinerlei Sinn die IInteger als Objecte zu bauen, das procedurale Interface mit overloaded Fuctions ist wesentlich effizienter und ausbaubarer. 2.) der nötige Programcode-"Überbau" damit IInterface mit TObject funktioniert reduziert die Performance bei sehr häufigen Aufrufen der Interfaces, ohne dabei Vorteile zu bringen (siehe 1.) Nutzt man Records hat man das alles selber unter Kontrolle und kann es hoch optimieren. 3.) die Speicherbereiche der Interfaces als Records sind "prealloziert". D.h. es ist nun ein leichtes über einen eigenen "Memory Manager" -> NMath.NPool -> die Speicherbereiche von freigegbenen Interfaces zwischenzuspeichern und später wiederzuverwenden. D.h. indirekt stellen die Interfacemethoden von IInteger, im speziellen die Allokatorfunktion und die Methode ._Release; die Schnittstelle zum internen Speichermanager NPool dar. Denn exakt zum autom. Aufruf von ._Release + ._RefCount = 0 wird der Interface Record als freier Record im Pool des DEC Speichermanagers eingeordnet. Das hat zB. zur Folge das bei der Berechnung von Pi zu 1 Million Dezimalstellen normalerweise ca. 250.000 mal ein Speicherbereich als Record-Interface vom Borland Speichermanager erzeugt werden müsste. Der Speicherpool NPool reduziert aber diese 250.000 Aufrufe auf ca. 1.000 tatsächlich Speicheranforderungen an den Borland MM. Im Falle Elliptischer Curven Berechnungen habe ich dabei eine Performancsteigerungvon ca. 10% erreicht. D.h. die IInteger Interfacemethoden stellen die Grndlage dafür dar, das man deren Speicherbereiche durch einen eigenen und optimierteren Speichermanager verwalten kann. 4.) das der Speichermanager im DECMath nun fast unabhängig vom Borland MM ist kann dieser auch speziell für Multi-Threaded Berechnungen optimiert werden. -> heist: NPool arbeitet Thread bezogen, jeder Thread installiert seinen eigenen NPool -> ergo bei IInteger-Allokationen verschiedener Thread kommen diese sich nur minimal in die Quere -> ergo kein Locking per RTLCriticalSection's wird benötigt -> ergo viel mehr Performance 5.) da der NPool als stark frequentierte Schnittstelle arbeitet, kann er dazu benutzt werden um ein Polling auf GUI Ebene durchzuführen. D.h. NPool ist auch verantwortlich das Computations-Management zu steuern, sprich über Benutzerinstallierbare Callback Interfaces kann der Programmierer periodisch Application.ProcessMesages aufrufen, oder in der Callback übder Abort eine Berechnung abbrechen, oder den zeitlichen- berchnungsmäßigen Fortschritt anzeigen. Für all diese Operationen muß der Programmierer aber NICHT in seinem mathematischem Code rumwurschteln. Man kennt das ja: man baut komplizierte Funktionen und muß darin das GUI mit Progressbars, Buttonabfragen usw. einbauen. Man muß also zeri komplette getrennte Sachen vermischen und verliert den Überblick. Nun, NPool implementiert also ein asynchrones, intervall-zeitgesteuertes Polling während der mathematischen Berechnungen. 6.) da nur die internen Funktionen im DECMath einen IInteger allozieren können, spricht man auch von Auto-allokatoren. D.h. du als Benutzer kümmerst dich NIE um die Allozierung, Zuweisung, Kopierung bei Veränderungen von IInteger Interfaces. Somit snd IInteger wie zB. LongStrings der RTL, Datenspeicher mit Autoallokation, Garbage Collections und Copy on Write Demand Feature. Du kannst als zB. auch
Delphi-Quellcode:
Schaue mal hier in die Code Library rein, da müsste es einen weitergehenden Beitrag zu meinem Interface-Trick -> "forged Interface" zu finden sein.
var
A,B,C: IInteger; begin // 1.) <- A,B,C werden automatisch mit NIL initialisiert NSet(A, 1); // 2.) <- A wurde intern automatisch alloziert und mit 1 gefüllt B := A; C := B; // 3.) <- Zuweisung von Variablen bedeutet das A,B,C auf das gleiche Object zeigen, wie bei Strings NInc(B); // 4.) <- Veränderungen an B im Inhalt führen zum "Copy on Write Demand" Feature, // B bekommt ein neue Interfacekopie alloziert, mit Inhalt '1' C := B; // 5.) <- Interface A,C wird im refCounting um 1 dekremetiert, // und C zeigt auf B, B Interface +1 im RefCounter // 6.) <- über unsichtbare try finally Blöcke sind ALLE Interface Variablen im Delphi geschützt. // hier also try finally, mit _Release(InterfaceArray[0..2]->@A) // der Compiler legt also die Variablen A,B,C so im Speicher ab das sie in einem Array[0..2] of IInteger liegen // somit kann er schnellere, Looporientierte Funktionen zum Freigeben dieser Interfaces benutzen. end; Gruß Hagen PS: ein IInteger wie A := nil wird mathematisch als 0 betrachtet, so gesehen gibt es KEINEN logischen Zusammenhang zwischen einen allozierten IInteger Interface und einem NIL Interface. Mathematisch sind BEIDES Zahlen, die eine <> 0 die andere == 0. Dies vereinfacht die Denkweise wie man IInteger benutzen kann, sie sind fast ordinale Datentypen wie Integer, Comp, Real, Double etc. Am besten vergleichbar mit LongStrings. Historisch gesehen sind IInteger die 3. Generation, nach zwei versuchen mit Objecten die wesentlich unhandlicher waren. |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#77
Hier mal die Linkszu weitergehenden Erklärungen:
http://www.delphipraxis.net/internal...ct.php?t=14605 http://www.delphipraxis.net/internal...ect.php?t=7357 Gruß hagen |
Zitat |
Registriert seit: 12. Aug 2004 49 Beiträge |
#78
Hi Hagen
die beiden links sind gold wert nach ein paar gemütlichen stunden mit dem debugger (*ironie*^^) habe ich erstmal die ganze genialität der sache regestriert^^ alles habe ich noch nicht raus....aber kommt noch ich versuch es mal zusammenzufassen: (ich hoffe du korrigierst mich falls ich irgendwas falsch interpretiere^^) im grunde nutzen wir nur ein verhalten vom compiler das er bei Interfaces zeigt.... Interfaces wie sie hier benutzt werden sind im prinziep nur zeiger auf eine VMT wobei uns der compiler ein haufen arbeit abnimmt.... im grunde kann man sie wie objekte/klassen verwenden.... 1. locale interface variable var MyIInteger: IInteger hier wird MyIInteger auf dem stack angelegt und mit nil initialisiert das besondere hierbei ist das sie mit nil intialiesiert wird was bei anderen localen var (z.B. Pointer, Integer) nicht der fall wäre, die haben irgendwelche werte die vorher auf dem stack lagen.... (das so erzeugte MyIInteger würde ich mir in gewisser weise als Pointer vorstellen) 2. funktions/procedur - aufrufe die so ein Interface als parameter haben NSet(MyIInteger,1); die funktionen testet ob MyIInteger nil ist if @MyIInteger <> nil then...... wenn sie nil ist dann wird eine funktion aufgerufen die dem interface eine VMT zuweist und speicher reserviert.... dazu komme ich gleich noch wenn sie nicht nil ist hat sie eine VMT und ist damit bereits initialisiert und kann verwendet werden.... 3. zuweisungen MyIInteger1:= MyIInteger2 hier ruft der compiler automatisch IntfCopy auf procedure _IntfCopy(var Dest: IInterface; const Source: IInterface); dadurch wird bei Dest der refcounter um 1 erhöht und MyIInteger1 auf die VMT von MyIInteger2 geändert und bei MyIInteger1 wird der refcounter um 1 verringert... fals er 0 wird, wird die VMT und der speicher von MyIInteger1 frei gegeben beide interface zeigen jetzt auf die selbe VMT..... dabei wird kein neuer speicher reserviert... halt wie bei Pointern 4. wenn der gültigkeitsbereich endet also wenn das ende der funktion erreicht ist wird vom compiler automatisch IntfClear aufgerufen function _IntfClear(var Dest: IInterface): Pointer; dabei wird der refcounter von MyIInteger um eins verringert wird er 0, wird die VMT und der speicher von MyIInteger frei gegeben auch bei MyIInteger:= nil; wird der refcount um 1 für die variable verringert 5. die VMT (Virtual Methode Tabel) jedes interface hat eine VMT
Delphi-Quellcode:
diese 3 methoden müßen von klassen die ein interface nutzen implementiert werden, das macht man am einfachsten in dem man TInterfacedObject als vorfahre der klasse mit einbindet
type
IInterface = interface ['{00000000-0000-0000-C000-000000000046}'] function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; end; da unser interface MyIInteger in keine klasse eingebunden ist funktioniert das so nicht deshalb müßen wir unsere VMT mit den 3 funktionen selber machen jede funktion die als übergabeparameter eines unserer interfaces hat, prüft ob das interface auf nil zeigt ist das der fall reservieren wir speicher für ein pointerarray das min. 3 einträge hat die 3 pointer müßen wir natürlich auf funktionen zeigen lassen die wir selber noch implementieren müssen... die im prinzip das machen was die standartfunktionen eines interfaces machen außerdem können wir hier auch noch unsere eigenen methoden mit einbauen... indem wir die VMT mit mehr einträgen machen (>3) auch felder sind möglich, was bei normalen interfaces nicht möglich wäre Fazit: der compiler übernimmt bei interfaces das was man bei normalen objecten mit create oder free selber machen müßte... man muß sich nicht um das aufräumen kümmern... speicherlecks sind eigentlich ausgeschlossen allerdings muß man sich um die VMT selber kümmern... das ist zwar im ersten augenblick komplizierter, aber wenn es einmal gemacht ist kann man nie wieder das freigeben oder erstellen von objekten vergessen in c++ ruft der compiler automatisch den construktor für ein objekt auf wenn es als lokale variable verwendet wird...ebenso den destruktor (natürlich kann man hier auch noch den zeiger auf ein objekt verlieren und ein speicherleck bekommen...) schade das sowas nicht bei delphie automatisch geht... darum der umweg über die interfaces aber großes plus bei interfaces ist das refcounting und kein TObject *juhu* den overhaed den TObjekt verursacht... *omg* da haben meine klassen wehniger eigene funktionen und felder als das was sie von TObject aufgedrückt bekommen.... eigentlich auch schade das man klassen nicht ohne TObjekt erstellen kann andererseits hat die VCL auch vorteile was besonders das RAD betrifft... interaktionen mit dem benutzer der anwendung werden doch extrem vereinfacht... naja, ist bissel verworen was ich da geschrieben habe... aber ich denke mal das es halbwegs richtig ist^^ ich schnapp mir morgen erstmal das asm-buch.... hab mit tasm noch nix gemacht... nur masm... und da wird einiges anders sein bei pascal lustig fand ich nur das ich die asm-funktion von waitcursor schneller kapiert habe als das delphie-construckt^^ kleiner schneller sauberer naja... ich lerne ja erst noch den delphi-syntax mfg Dano |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#79
Hi Dano,
das war im Grunde alles korrekt, und ein Kompliment von mir weil dues ziemlich schnell erfasst hast. Es gibt aber vielleicht noch einige kleinere Richtigstellungen: 1.) Delphi implementiert bei Interface Variablen ausschließlich das korrekte Initialisieren mit NIL und das korrekte Finalisieren mit NIL, mehr nicht. Diese beiden Aufgaben macht Delphi in geschützten Codeblöcken, also unsichtbaren try finally end; Blöcken. Werden mehrere Interface-Variablen in einer Procedure benutzt so gruppiert der Compiler diese Variablen im Stack immer als Array[] of IInterface. Deren Initialisierung und Finalisierung mit NIL wird dann durch Funktionen der RTL erledigt die ganz Arrays of IInterface auf NIL setzen. Das "auf NIL" setzen bedeutet das die RTL erstmal überprüft ob die Variable <> nil ist, wenn ja wird Valiable._Release; aufgerufen und Variable := nil; gesetzt. 2.) Interfaces können, müssen aber nicht das Reference Counting implementieren. Wenn sie es tuen dann nur aus dem Grunde um sich selber korrekt zerstören zu können. D.h. die Implementierung der Allokation + Deallokation der Interfaces wird nur durch die Interfaces erledigt. Dies beiden Punkte stellen das "Garbarge Collection" Feature dar. 3.) Die proceduralen Schnittstellen im DECMath -> math. Funktionen, rufen intern bei nicht-readonly Variablen immer eine Funkton auf. Diese Funktion hat drei Aufgaben, a.) Überprüfung ob Variable ein nil Interface enthält und falls ja ein neues IInteger Intrface allozieren, und wenn nein b.) Variable enthält ein Interface also Überprüfung ob .RefCount > 1 ist, wenn ja neues IInteger Interface allozieren und Inhalt aus dem alten ind das neue kopieren. Der Kopierschritt wird aber durch einen Parameter gesteuert. c.) am Schluß gibt die Funktion einen Zeiger auf einen Record zurück -> sozusagen wndelt sie den Datentyp Pointer(IInteger) in PIIntegerRecord(IInteger) um. Diese Funktion stellt also das "Copy on Write Demand" und "Autoallokation" Feature zur Verfügung. In den meisten Fällen sieht das so aus:
Delphi-Quellcode:
NAllocCopy() ruft intern NAlloc(A, True) auf, und diese Funktion führt obigen Punkt 3. aus.
procedure NInc(var A: IInteger; B: Integer);
begin with NAllocCopy(A)^ do ..... end; Durch die Deklaration mit "var A: IInteger" haben wir ja eine "by Reference" Aufrufkonvention, wir bekommen den Zeiger auf die Zeigervariable in der das IInteger steht, und somit können wir diesen Variablen neue IInteger Interfaces zuweisen. Es dürfte aber klar sein das solche Funktionen möglichst hochoptimiert sind. Im Falle von NAlloc() wird intern in den meisten Fällen nur 3 Assembler Instruktionen ausgeführt. Leider kennt Delphi keinen Precompiler und somit keine Makros. Man sollte also wenn man mit Interfaces arbeitet immer die Aufrufkonventionen "var" oder "const" benutzen, dies ist wichtig. 4.) Interfaces sind keine Zeiger auf VMT's !! Sie sind Zeiger auf einen Speicherbereich in dem die ersten 4 Bytes == Pointer ein Zeiger gespeichert wurde der der Zeiger auf eine VMT ist. Nach diesem Pointer kommen noch eventuell Daten, die verschiedenen Felder/Member des allozierten Interface Objectes. Im Falle von IInteger also ein Zeiger auf ein Array[]of Cardinal, eine Count, Size Variable und Negative um das Vorzeichen zu speichern. IInteger Interface Variablen könnte man also direkt zu einem Record casten und somit auf die internen Daten zugreifen. Man sollte das aber nicht machen So, und alle diese Features waren noch vor dem ersten eingetippten Source ein zu erreichendes Ziel, d.h. ich habe mich nur deshalb für Interfaces und prozeduraler Schnittstelle entschieden da das die einzigste Möglichkeit im Delphi seinerzeit war. Gruß hagen |
Zitat |
Registriert seit: 12. Aug 2004 49 Beiträge |
#80
hi Hagen
ich habe mal ne kleines test-interface zusammengebastelt ich deklariere ein interface initialisiere mit einer function meine felder und die VMT soweit läuft alles prima *juhu* nun bereiten mir aber aufrufe denen ich einen parameter übergebe probleme
Delphi-Quellcode:
wenn ich TestInterface.Name:='Hallo! ;)';
aufrufe pusht er zuerst den zeiger auf den string
type
IMyInterface = Interface(IInterface) function GetName: String; stdcall; procedure SetName(Name: String); stdcall; function GetRefCount: Integer; stdcall; property Name: String read GetName write SetName; end; dann pusht er den zeiger auf meinen interface record dann call't er die funktion SetName(Name: String) soweit alles ok *freu* jetzt kommt der übliche stackrahmen (push ebp, mov ebp,esp......) und dann will er den referenzzähler des string erhöhen, und das geht schief der holt sie den falschen zeiger vom stack ( -> exception).... generell verwechselt er die var's aufm stack die sind alle um eins verschoben..... ich denke mal das das interface automatisch ein self aufn stack haut(macht er ja auch), und vom compiler wird das in der function nicht berücksichtigt gibts für das problem ne lösung? mfg Dano |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |