Packing/Bitpacking

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Packing/Bitpacking

Beitrag von Marsmännchen »

Hi,

ich bin jetzt in der Freepascal-Doku bis zum Thema 'Structured Types' vorgestoßen. Da gibt es etwas, was ich zwar vom Handling bzw. Ablauf her verstehe, aber mir erschließt sich der Sinn nicht so recht. Es geht um das Packing/Bitpacking solcher Strukturen (s. Reference Guide Seite 35).
Also man soll als Programmierer keine Annahmen darüber treffen, wie der Compiler so eine Struktur im Speicher anlegt... okay, also nicht mit Pointern wild rumschießen.
Aber man kann über Direktiven ($BITPACKING usw.)den Compiler dazu bringen, die Strukturen zu packen. Er ordnet sie dann entlang einer Bytegrenze an. Klingt nach Voodoo, aber ich vermute mal, die ganze Aktion bringt die Werte der Struktur dann halt doch in eine gewisse Linie. Damit handelt man sich aber wiederum Restriktions ein: Man kann die Adresse nicht mehr so ohne weiteres auslesen und die Struktur nicht mehr als Var-Parameter benutzt werden (es geht dann in gewissen Grenzen doch wieder). Also wozu sollte man das dann tun? Was hat man davon?

Das ganze hört sich nicht so an, als ob ich es als Hobby-User allzu oft brauchen würde, aber wenn man sich damit beschäftigt hat, möchte ich wenigstens wissen, wozu diese Pack-Geschichte gut sein soll. Ich hab jetzt im Forum keine Threads dazu gefunden. Könnt ihr mich aufklären (nein, nicht über die Bienen und Blumen :lol: )? Wofür ist das gut?
Ich mag Pascal...

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Packing/Bitpacking

Beitrag von Socke »

Marsmännchen hat geschrieben:Also man soll als Programmierer keine Annahmen darüber treffen, wie der Compiler so eine Struktur im Speicher anlegt... okay, also nicht mit Pointern wild rumschießen.
Hier geht es insbesondere um Records, Klassen und Objekte. Bei den primitiven Datentypen, Strings und Arrays ist das gut dokumentiert und man darf durchaus davon ausgehen, dass z.B. die Elemente eines Arrays im Speicher direkt hintereinander liegen.
Marsmännchen hat geschrieben:Aber man kann über Direktiven ($BITPACKING usw.)den Compiler dazu bringen, die Strukturen zu packen. Er ordnet sie dann entlang einer Bytegrenze an. Klingt nach Voodoo, aber ich vermute mal, die ganze Aktion bringt die Werte der Struktur dann halt doch in eine gewisse Linie.
Beim Packen geht es um Speicheroptimierung. Per Standard legt der Compiler die Daten so im Speicher ab, dass der Prozessor optimal darauf zugreifen kann. Bei gepackten Records muss der Prozessor also ggf. mehr arbeiten um an den gewünschten Wert zu kommen.

Mit bitpacked kann man außerdem direkt einzelne Bits ansprechen. Z.B. kann man einfach sagen: Von einem Byte haben die ersten 4 Bits die Bedeutung X, die nächsten 2 die Bedeutung Y und die restlichen 2 sind für Z reserviert. Andernfalls müsste man im Programm jede Menge Bits durch die Gegend schieben, was das Programm schwierig zu lesen macht.

Hier ein paar Beispiele:

Code: Alles auswählen

program Project1;
 
type
  TInt1 = 0..15; // Hierfür werden maximal 4 Bit benötigt
  TUnpackedRanged = record
    v1: TInt1; // es wird aber 1 Byte reserviert
    v2: TInt1;
  end;
  TPackedRanged = packed record
    v1: TInt1; // Packen auf Byte-Grenzen bringt hier nichts
    v2: TInt1;
  end;
  TBitPackedRanged = bitpacked record
    v1: TInt1; // beide Elemente passen in ein Byte
    v2: TInt1;
  end;
 
  TBitAccess = record // Zugriff auf ein Byte entweder als Ganzes oder auf jedes Bit
  case integer of
    0: (ByteValue: Byte;);
    1: (BoolValue: bitpacked array[0..7] of Boolean;);
  end;
  TBoolDemo = record // hier stimmen die Speicherbereich des Bytes und des Arrays nicht mehr überein (ByteValue ist identisch mit BoolValue[0])
  case integer of
    0: (ByteValue: Byte;);
    1: (BoolValue: array[0..7] of Boolean;);
  end;
 
var
  ba: TBitAccess;
  b: Boolean;
begin
  Writeln(sizeof(TUnpackedRanged));  // 2 Byte
  Writeln(sizeof(TPackedRanged));    // 2 Byte
  Writeln(sizeof(TBitPackedRanged)); // 1 Byte
  Writeln(Sizeof(TBitAccess));       // 1 Byte
  Writeln(sizeof(TBoolDemo));        // 8 Byte
 
  ba.ByteValue := $F0;
  for b in ba.BoolValue do
    Writeln(b);
  readln;
end.
Hier gilt: da das Speicherlayout dokumentiert ist, darf man hierüber auch Annahmen treffen.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Re: Packing/Bitpacking

Beitrag von Marsmännchen »

Hi,

wow, das ist aber eine schnelle Antwort. Danke vor allem für die Beispiele. Ohne die hätte ich es wohl doch nicht verstanden.
Socke hat geschrieben:Hier geht es insbesondere um Records, Klassen und Objekte. Bei den primitiven Datentypen, Strings und Arrays ist das gut dokumentiert und man darf durchaus davon ausgehen, dass z.B. die Elemente eines Arrays im Speicher direkt hintereinander liegen.
Okay, das sehe ich ein, ansonsten würde es ja auch relativ wenig Sinn machen, dem Programmierer überhaupt Pointer anzubieten.
Socke hat geschrieben: Beim Packen geht es um Speicheroptimierung. Per Standard legt der Compiler die Daten so im Speicher ab, dass der Prozessor optimal darauf zugreifen kann. Bei gepackten Records muss der Prozessor also ggf. mehr arbeiten um an den gewünschten Wert zu kommen.
Kann man daraus die Faustregel ziehen: Wenn es um große Datenmengen geht, dann sollte man Packing/Bitpacking in Erwägung ziehen und wenn es eher um Geschwindigkeit in der Ausführung geht (zB Spiele), dann lässt man eher die Finger davon? Also mal unabhängig davon, dass man sich das sicherlich bei jedem Projekt im Einzelfall überlegen muss?
Socke hat geschrieben: Mit bitpacked kann man außerdem direkt einzelne Bits ansprechen. Z.B. kann man einfach sagen: Von einem Byte haben die ersten 4 Bits die Bedeutung X, die nächsten 2 die Bedeutung Y und die restlichen 2 sind für Z reserviert. Andernfalls müsste man im Programm jede Menge Bits durch die Gegend schieben, was das Programm schwierig zu lesen macht.
Das ist cool. Das ist so ähnlich wie die Zugriffsverwaltung von Linux für Dateien, oder? Also rwx-Bits und die auch noch nach User, Groups etc. aufgeteilt und das alles in einem Byte. Oder habe ich das jetzt missverstanden? Damit kann man Informationen ultrakompakt.... halt packen.

Eine allgemeine Frage drängt sich mir aber noch auf: Früher war Speicher sehr teuer und man hat tunlichst darauf geachtet, ihn effizient zu nutzen (ich weiß wovon ich spreche: mein erster Homecomputer hatte 1KB RAM!). Heutzutage steht in Computerbüchern durchaus, dass das inzwischen keine große Rolle mehr spielt, weil Speicher praktisch immer massenhaft vorhanden ist. Ist das Packen also eher ein Relikt aus alten TP-Zeiten? Oder kann man damit in der heutigen Praxis doch noch merkbar optimieren?

Auf jeden Fall ist diese Thematik offenbar grundlegender für die Benutzung von Freepascal, als ich anfangs dachte.
Ich mag Pascal...

wp_xyz
Beiträge: 5151
Registriert: Fr 8. Apr 2011, 09:01

Re: Packing/Bitpacking

Beitrag von wp_xyz »

Naja, heute in der Arduino/RasPi-Zeit wird Speicher schon mal wieder kostbar... Ansonsten kannst du das Thema vergessen, bis auf eine Ausnahmen: binäre Dateien. Wenn du Records bestehend z.B. aus 1 Byte und 1 Word schreibst, steht normalerweise das Word nicht direkt hinter dem Byte anschließend, sondern es werden je nach Rechnerarchitektur/Compiler etc. mehr oder weniger Füllbytes eingefügt. Als Folge kannst du die Datei evtl nicht mehr auf jedem Rechner lesen. In diesen Fall ist es besser, dem Compiler durch entsprechende Packed-Direktiven vorzugeben, wie die Recordelemente ausgerichtet werden sollen. Ich nehme in der Regel immer "packed record" (oder "packed array") was die Elemente dicht an dicht zusammenschiebt.
Zuletzt geändert von wp_xyz am Do 11. Feb 2016, 23:59, insgesamt 1-mal geändert.

Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Re: Packing/Bitpacking

Beitrag von Marsmännchen »

Okay, Raspberry hatte ich nicht auf dem Schirm, weil ich mit solchen Teilen noch nicht rumgemacht habe. Auf jeden Fall nehme ich mit: will man mit seinem Code portabel bleiben, dann sollte man das Packen von binären Dateien im Hinterkopf behalten (ist ja eigentlich ne üble Falle: wer weiß sowas schon, wenn man nicht zufällig im Forum dumme Fragen gestellt hat 8) ?)
Ich mag Pascal...

Mathias
Beiträge: 6918
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Packing/Bitpacking

Beitrag von Mathias »

Bei einer grösseren Boolean-array macht bitpacked auch sehr viel Sinn.

Code: Alles auswählen

var
  a1: array [0..31] of boolean;
  a2: bitpacked array [0..31] of boolean;
begin
  ShowMessage(IntToStr(SizeOf(a1)) + ' ' + IntToStr(SizeOf(a2))); 
Der Speicher-Verbrauch ist um das 8-Fache kleiner.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Re: Packing/Bitpacking

Beitrag von Marsmännchen »

Hmmm.... also ist diese Packing-Thematik heutzutage nur(?) für eine Reihe von Spezialfällen relevant. Gibt es eine Faustregel, wann man Packing in Betracht ziehen sollte, oder soll man einfach auf Verdacht mal so einen Speichervergleich mit gepackten/ungepackten Strukturen ausprobieren und dann sehen, ob es sich lohnt?
Ich mag Pascal...

Komoluna
Beiträge: 565
Registriert: So 26. Aug 2012, 09:03
OS, Lazarus, FPC: Windows(10), Linux(Arch)
CPU-Target: 64Bit

Re: Packing/Bitpacking

Beitrag von Komoluna »

Ein Nachteil von Bitpacking ist, dass es den Speicherzugriff verlangsamt, da das Programm trotzdem ein größeres Datenfeld einliest und herum-bit-shiftet und den Rest dann ignoriert, auch wenn du nur ein einzelnes Bit haben willst. Gelesen wird aber trotzedem der gesamte Block.

In den meisten Fällen ist das eher irrelevant, aber wenn man Code schreibt, der einige tausend mal pro Sekunde ausgeführt werden soll, nicht unbeträchtlich.

Als Faustregel: Wenn du Daten mit anderen Geräten austauscht(per TCP, in Dateien, etc.) solltest du darauf achten, die Daten zu packen, da sie sonst in einer Art "Raster" angeordnet werden, das sich von System zu System unterschiedlich sein kann.

MFG

Komoluna
Programmer: A device to convert coffee into software.

Rekursion: siehe Rekursion.

Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Re: Packing/Bitpacking

Beitrag von Marsmännchen »

Okay, ich werde mir das (Bit)Packing vorzugsweise für's Crosscompiling merken. Danke für eure Auskünfte. Ohne euch hätte ich es bestimmt nicht verstanden :)
Ich mag Pascal...

Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Re: Packing/Bitpacking

Beitrag von Marsmännchen »

... sorry, ich muss nochmal was fragen. In der FP-Referenz bin ich auf folgende Passage gestoßen:
Remark: Free Pascal also supports the packed object. This is the same as an object, only the elements (fields)
of the object are byte-aligned, just as in the packed record. The declaration of a packed object is
similar to the declaration of a packed record :
Type
TObj = packed object
Constructor init;
...
end;
Pobj = ^TObj;
Var PP : Pobj;
Similarly, the {$PackRecords } directive acts on objects as well.
Ich verstehe das Beispiel nicht so recht.
Da wird ein Pointer Pobj in das Objekt eingeführt. Und dieser Pointer wird dann an eine Variable PP übergeben. Hä? Hätte es nicht gereicht, TObj als packed object zu deklarieren um eine "Packung" zu erreichen? Wozu jetzt dieser Pointer? Dass ein packed object ähnlich funktioniert wie ein entsprechender Record überrascht mich nun nicht gewaltig. Oder soll ich das erstmal beiseite tun und mich mit den Grundlagen beschäftigen, bevor ich mich mit solchen Spezialthemen rumschlage???
Ich mag Pascal...

wp_xyz
Beiträge: 5151
Registriert: Fr 8. Apr 2011, 09:01

Re: Packing/Bitpacking

Beitrag von wp_xyz »

Mit "object" deklarierte Objekte stammen aus der Anfangszeit der Objektorientierung in Pascal und kommen heute kaum mehr vor. Der von einer als "TObj" deklarierten Variablen belegte Speicher liegt auf dem Stack, wie bei einem Record. Wenn man das Objekt auf dem Heap haben will, muss man explizit Speicher anfordern ("New") und wieder freigeben ("Dispose"), und dafür braucht man den Pointer.

Die heute als "class" deklarierten Objekte liegen auf dem Heap, die Speicheranforderung erfolgt automatisch durch den Constructor, die Freigabe durch den Destructor. Durch Einiges an Compiler-Magic wird verschleiert, dass es sich bei Variablen vom Typ "TMyClass = class" eigentlich um Pointer handelt.

Mathias
Beiträge: 6918
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Packing/Bitpacking

Beitrag von Mathias »

Mit "object" deklarierte Objekte stammen aus der Anfangszeit der Objektorientierung in Pascal und kommen heute kaum mehr vor.
Es gibt zum Teil Anwendungen, wo man besser mit Object als mit Class bedient ist, aber wie du sagst, es wird sehr selten gebaucht.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Re: Packing/Bitpacking

Beitrag von Marsmännchen »

Danke für eure Erläuterungen, dann ist mir die Sache jetzt klar. Ich hatte den Code aus der Referenz auch erst nicht richtig verstanden. Ich habe das 'end;' übersehen und dachte, der Pointer gehört noch direkt zu dem Objekt... Anfängerfehler. Aber ohne eure Hinweise hätte ich trotzdem den Sinn der ganzen Geschichte nicht gesehen. Das ist halt das Problem mit Referenzen. Sie sind mehr auf die Technik ausgerichtet. Ich habe schon angefangen, mir selbst zu verschiedenen Themen Tutorials zu schreiben. Mal sehen, vielleicht wird ja später mal ein Buch draus :mrgreen: .
wp_xyz hat geschrieben:... Durch Einiges an Compiler-Magic wird verschleiert, dass es sich bei Variablen vom Typ "TMyClass = class" eigentlich um Pointer handelt.
Ist meines Wissens bei anderen Programmiersprachen auch net anders 8)
Mathias hat geschrieben:Es gibt zum Teil Anwendungen, wo man besser mit Object als mit Class bedient ist, aber wie du sagst, es wird sehr selten gebaucht.
Wenn man bedenkt, dass Objects auf dem Stack liegen, der seine Operationen üblicherweise schneller erledigt, als der Heap, dann dürften Objects gut für zeitkritische Geschichten sein. Richtig?
Ich mag Pascal...

Antworten