Gibt es einen Vorteil bei der Verwendung des alten Objekts "Objekt" anstelle von "Klasse" in Delphi?

8

In Delphi vernünftig verwenden Leute% ce_de%, um Objekte zu definieren.
In Turbo Pascal für Windows verwendeten wir class und heute können Sie object noch verwenden, um ein Objekt zu erstellen.

Der Unterschied besteht darin, dass ein object auf dem Stapel lebt und ein object auf dem Heap lebt.
Und natürlich wird class abgeschrieben.

Alles beiseite legen:

Gibt es einen Vorteil zu haben, Geschwindigkeit, indem Sie object anstelle von Klasse verwenden?

Ich weiß, dass object in Delphi 2009 gebrochen ist, aber ich habe einen speziellen Anwendungsfall 1) wo Geschwindigkeit zählt und ich versuche herauszufinden, ob% co_de verwendet wird % wird mein Ding schneller machen, ohne es fehlerhaft zu machen.
Diese Codebasis ist in Delphi 7, aber ich kann sie nach Delphi 2007 portieren, habe mich noch nicht entschieden.

1) Conways Spiel des Lebens

Langer Kommentar
Danke, dass Sie mich in die richtige Richtung weisen.

Lass mich etwas mehr erklären. Ich versuche eine schnellere Implementierung von hashlife , siehe auch hier oder hier für einfachen Quellcode

Der aktuelle Rekordhalter ist golly , aber golly verwendet eine direkte Übersetzung von Bill Gospher original Lisp Code (der als Algorithmus brillant ist) , aber nicht auf der Mikroebene überhaupt optimiert). Hashlife ermöglicht es Ihnen, eine Generation in O (log (n)) Zeit zu berechnen.

Dies geschieht durch Verwendung einer Raum / Zeit-Abwägung. Aus diesem Grund benötigt hashlife viel Speicher, Gigabyte sind nicht unbekannt. Im Gegenzug können Sie die Generation 2 ^ 128 (340282366920938463463374607431770000000) mit der Generation 2 ^ 127 (170141183460609231731687303715880000000) in o (1) Zeit berechnen.

Da hashlife Hashwerte für alle Teilmuster, die in einem größeren Muster vorkommen, berechnen muss, muss die Zuweisung von Objekten schnell erfolgen.

Hier ist die Lösung, auf die ich mich eingelassen habe:

Zuordnungsoptimierung
Ich reserviere einen großen Block physischen Speichers (benutzerdefinierbar), sagen wir 512 MB. In diesem Blob gebe ich das zu, was ich Käsestapel nenne. Dies ist ein normaler Stack, bei dem ich pushe und pop, aber ein Pop kann auch aus der Mitte des Stacks kommen. Wenn das passiert, markiere ich es in der object Liste (dies ist ein normaler Stapel). Beim Drücken überprüfe ich zuerst die object Liste, wenn nichts frei ist, schiebe ich wie gewohnt. Ich werde Datensätze verwenden, wie es die Lösung mit der geringsten Menge an Overhead sieht.

Aufgrund der Funktionsweise von hashlife findet nur sehr wenig free ping statt und viele free es. Ich halte separate Stapel für Strukturen unterschiedlicher Größe und sorge dafür, dass der Speicherzugriff auf 4/8/16 Byte-Grenzen ausgerichtet bleibt.

Andere Optimierungen

  • Rekursion entfernen
  • Cache-Optimierung
  • Verwendung von pop
  • Vorberechnung von Hashes (ähnlich wie bei Rainbow-Tabellen)
  • Erkennung pathologischer Fälle und Verwendung des Fallback-Algorithmus
  • Verwendung von GPU
Johan 23.05.2011, 22:28
quelle

4 Antworten

12

Bei Verwendung der normalen OOP-Programmierung sollten Sie immer die class art verwenden. Sie haben das leistungsfähigste Objektmodell in Delphi, einschließlich der Schnittstelle und Generika (in späteren Delphi-Versionen).

1. Datensätze, Zeiger und Objekte

Datensätze können böse sein (langsam versteckte Kopie, wenn Sie vergessen haben, einen Parameter als const zu deklarieren, versteckten langsamen Bereinigungscode aufzuzeichnen, ein fillchar würde jede Zeichenfolge in einem Datensatz zu einem Speicherleck machen ...), aber sie sind manchmal sehr praktisch, um über einen Zeiger auf eine binäre Struktur (zB einen "kleinen Wert") zuzugreifen.

Ein dynamisches Array winziger Datensätze (z. B. mit einer ganzen Zahl und einem doppelten Feld) ist viel schneller als ein TList kleiner Klassen; mit unserem TDynArray wrapper , haben Sie High-Level-Zugriff auf die Datensätze, mit Serialisierung, Sortierung, Hashing und so weiter.

Wenn Sie Zeiger verwenden, müssen Sie wissen, was Sie tun. Es ist definitiv besser, mit Klassen zu bleiben, und TPersistent , wenn Sie das magische "VCL-Komponentenbesitzmodell" verwenden möchten.

Die Vererbung ist für Datensätze nicht zulässig. Sie müssen entweder einen "variant record" verwenden (indem Sie das Schlüsselwort case in seiner Typdefinition verwenden), entweder verschachtelte Datensätze verwenden. Wenn Sie eine C-ähnliche API verwenden, müssen Sie manchmal objektorientierte Strukturen verwenden. Verschachtelte Datensätze oder Variantensätze zu verwenden, ist IMHO viel weniger klar als das gute alte "Objekt" Vererbungsmodell.

2. Wann wird das Objekt verwendet?

Aber es gibt einige Orte, wo Objekte eine gute Möglichkeit sind, auf bereits existierende Daten zuzugreifen.

Sogar das Objektmodell ist besser als das neue Datensatzmodell, weil es einfache Vererbung handhabt.

In ​​einem Blog-Eintrag letzten Sommer , Ich habe einige Möglichkeiten gepostet, Objekte noch zu benutzen:

  • Eine Memory-Mapped-Datei, die ich sehr schnell analysieren möchte: Ein Zeiger auf ein solches Objekt ist einfach großartig, und Sie haben immer noch Methoden zur Hand; Ich verwende dies für TFileHeader oder TFileInfo, die den .zip-Header in SynZip.pas zuordnen;

  • Eine Win32-Struktur, wie von einem API-Aufruf definiert, in der ich praktische Methoden für den einfachen Zugriff auf die Daten einfüge (dafür können Sie zwar einen Datensatz verwenden, aber wenn es eine Objektorientierung in der Struktur gibt - was sehr ist) häufig - Sie müssen Datensätze verschachteln, was nicht sehr praktisch ist);

  • Eine auf dem Stack definierte temporäre Struktur, die nur während einer Prozedur verwendet wird: Ich verwende diese für TZStream in SynZip.pas oder für unsere RTTI-bezogenen Klassen, die die Delphi-generierte RTTI in einer objektorientierten Weise nicht abbilden als TypeInfo, das funktions- / prozedurorientiert ist. Durch die direkte Zuordnung des RTTI-Speicherinhalts ist unser Code schneller als die Verwendung der neuen RTTI-Klassen, die auf dem Heap erstellt wurden. Wir installieren keine Erinnerung, was für ein ORM-Framework wie das unsere gut für seine Geschwindigkeit ist. Wir brauchen eine Menge RTTI-Informationen, aber wir brauchen sie schnell, wir brauchen sie direkt.

3. Wie die Objektimplementierung im modernen Delphi gebrochen ist

Die Tatsache, dass das Objekt im modernen Delphi gebrochen ist, ist eine Schande, IMHO.

Normalerweise, wenn Sie einen Datensatz auf dem Stack definieren, der einige Referenz-Count-Variablen enthält (wie eine Zeichenkette), wird er durch einen Compiler-Magic-Code auf der Anfangsebene der Methode / Funktion initialisiert:

%Vor%

Diese _InitializeRecord und _FinalizeRecord werden "vorbereiten" und dann die O.Str-Variable "freigeben".

Mit Delphi 2010 habe ich herausgefunden, dass manchmal _InitializeRecord () nicht immer gemacht wurde. Wenn der Datensatz nur einige öffentliche Felder enthält, werden die versteckten Aufrufe manchmal nicht vom Compiler generiert.

Baut einfach die Quelle neu, und es wird ...

geben

Die einzige Lösung, die ich herausgefunden habe, war die Verwendung des Schlüsselwortes record anstelle von object.

So sieht der resultierende Code folgendermaßen aus:

%Vor%

Das {$ifdef UNICODE}record{$else}object{$endif} ist schrecklich ... aber der Code-Generierungsfehler ist seit ..

nicht aufgetreten

Die resultierenden Änderungen im Quellcode sind nicht riesig, aber ein bisschen enttäuschend. Ich habe herausgefunden, dass ältere Versionen der IDE (z. B. Delphi 6/7) diese Deklaration nicht analysieren können, daher wird die Klassenhierarchie im Editor unterbrochen ...: (

Die Abwärtskompatibilität sollte Regressionstests beinhalten. Viele Delphi-Benutzer bleiben wegen des vorhandenen Codes für dieses Produkt. Breaking Features sind sehr problematisch für die Delphi Zukunft, IMHO: Wenn Sie viel Code neu schreiben müssen, sollten Sie nicht nur das Projekt auf C # oder Java wechseln?

    
Arnaud Bouchez 24.05.2011, 06:11
quelle
7

Object war nicht die Methode Delphi 1 zum Einrichten von Objekten; es war die kurzlebige Turbo-Pascal-Methode zum Einrichten von Objekten, die in Delphi 1 durch das Delphi TObject-Modell ersetzt wurde. Es wurde aus Gründen der Rückwärtskompatibilität beibehalten, sollte aber aus mehreren Gründen vermieden werden:

  1. Wie Sie bemerkt haben, ist es in neueren Versionen gebrochen. Und AFAIK gibt es keine Pläne, es zu reparieren.
  2. Es ist ein konzeptionell falsches Objektmodell. Der ganze Punkt der objektorientierten Programmierung, die eine Sache von der prozeduralen Programmierung unterscheidet, ist die Liskow-Substitution (Vererbung und Polymorphismus), und Vererbung und Werttypen vermischen sich nicht.
  3. Sie verlieren die Unterstützung für viele Funktionen, die TObject-Nachkommen benötigen.
  4. Wenn Sie wirklich Werttypen benötigen, die nicht dynamisch zugeordnet und initialisiert werden müssen, können Sie stattdessen Datensätze verwenden. Du kannst nicht von ihnen erben, aber das kannst du auch mit object nicht so gut machen, also verlierst du hier nichts.

Was den Rest der Frage betrifft, gibt es nicht so viele Geschwindigkeitsvorteile. Das TObject-Modell ist sehr schnell, besonders wenn Sie den FastMM-Speichermanager verwenden, um die Erstellung und Zerstörung von Objekten zu beschleunigen. Wenn Ihre Objekte viele Felder enthalten, können sie sogar in vielen Fällen schneller als Datensätze sein, weil sie Sie werden als Referenz übergeben und müssen nicht für jeden Funktionsaufruf kopiert werden.

    
Mason Wheeler 23.05.2011 23:49
quelle
5

Wenn Sie die Wahl zwischen "schnell und möglicherweise unterbrochen" und "schnell und korrekt" haben, wählen Sie immer Letzteres.

Objekte im alten Stil bieten keinen Geschwindigkeitsanreiz gegenüber normalen alten Datensätzen. Wo immer Sie versucht sind, Objekte im alten Stil zu verwenden, können Sie stattdessen Datensätze verwenden, ohne das Risiko von nicht initialisierten Compiler-verwalteten Typen oder defekten virtuellen Methoden. Wenn Ihre Version von Delphi keine Datensätze mit Methoden unterstützt, verwenden Sie stattdessen nur eigenständige Prozeduren.

    
Rob Kennedy 23.05.2011 23:41
quelle
1

In älteren Versionen von Delphi, die keine Datensätze mit Methoden unterstützt haben, war die Verwendung von object der Weg, um Ihre Objekte auf dem Stack zu speichern. Sehr gelegentlich würde dies lohnende Leistungsvorteile bringen. Heutzutage ist record besser. Das einzige Feature, das in record fehlt, ist die Fähigkeit, von einem anderen record zu erben.

Sie geben viel auf, wenn Sie von class zu record wechseln, also denken Sie nur daran, wenn die Leistungsvorteile überwältigend sind.

    
David Heffernan 24.05.2011 06:36
quelle