Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Evrey

Treue Seele

  • »Evrey« ist der Autor dieses Themas

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

11

05.06.2014, 15:55

Dann hätte man ja ein maximales Alignment angeben können, z.B. 256 Bytes, oder eben ab solch einem Alignment auf einen anderen Algorithmus zurückfallen können. Bei Intel wird in der BPU immer zuerst erwartet, dass ein test im if als false ausfällt, würde also nicht gerade nennenswerte Einbuße in der Leistung bringen.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

12

05.06.2014, 16:09

Wozu benötigst du dieses "extreme Alignment" eigentlich genau?

Evrey

Treue Seele

  • »Evrey« ist der Autor dieses Themas

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

13

05.06.2014, 17:23

Nun, eine Arena ist in 65536 Zellen zerlegt, die z.B. im Falle LuaJITs 16 Bytes groß sein könnten. Das macht insgesamt 20 Bits an Adresse für die Zellen. Jetzt kann ein GC sehr schnell die Arena finden, die fürs Deallokieren zuständig wäre, indem einfach die entsprechenden Adress-Bits eines Objekts wegmaskiert werden. Ebenso fix gelangt man an den Zell-Index. Oder in Code:

C-/C++-Quelltext

1
2
3
4
5
6
7
// Arena des Objekts finden.
Arena* p = reinterpret_cast<Arena*>(
  (intptr_t{-1} << 20) & reinterpret_cast<intptr_t>(this)
);

// Zellen-Index des Objekts finden.
uint16_t _cell = 0xFFFF & (reinterpret_cast<uintptr_t>(this) >> 4);

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Evrey« (05.06.2014, 17:38)


David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

14

05.06.2014, 18:03

Ist das so performance-kritisch, dass eine zusätzliche Addition/Subtraktion (für Offset) schlimm wäre?

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

15

05.06.2014, 18:20

Ich habe meine Zweifel, dass eine Addition oder Subtraktion überhaupt theoretisch von der Geschwindigkeit einen unterschied macht. Und wenn doch , dann vielleicht einen so Bruchteil eines Taktes, der selbst in sehr Performance kritischen Code keinen messbaren Unterschied machen würde.

Evrey

Treue Seele

  • »Evrey« ist der Autor dieses Themas

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

16

05.06.2014, 18:32

Mit Addition/Subtraktion meinst du wohl das Offset, wie weit die Arena aus der Reihe des Alignments tanzt? An sich wäre das kein Drama, bloß stellt sich die Frage, wie ich an dieses Offset gelange. Es könnte jedes Vielfache der Page-Größe des Betriebssystems sein, völlig willkürlich. Dann müsste ich diese Offset-Information in jedem Objekt unterbringen, was diese nur unnötig aufbläht. Man könnte auch andersrum eine Tabelle anlegen, die "aligned" Arena-Adressen auf ihre tatsächlichen mapped. Dann allerdings wäre der (Cache-)Vorteil der fixen Maskierung völlig hinüber.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

17

05.06.2014, 18:36

Wenn du doch genau weißt wo die (nicht alignede) Arena genau beginnt, kannst du doch einfach diesen Pointer vom Objektpointer abziehen und schon hast du wieder das Offset in der Arena?

Evrey

Treue Seele

  • »Evrey« ist der Autor dieses Themas

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

18

05.06.2014, 19:00

Nö, das weiß ich ja nicht wirklich. Der GC bekommt von den zig Arenen nicht viel mit. Er schaut auf das Root-Objekt, rattert durch dieses durch, und findet die zuständigen Arenen über die Objekt-Adressen. Er führt keine zusätzliche Tabelle darüber, welche Arena welches Offset hat. Aber selbst wenn er das täte... Für jedes Objekt nach dem richtigen Offset zu schauen könnte ganz schön ziehen. D;

Edit:

Zitat

If the dwFreeType parameter is MEM_RELEASE, this parameter must be the base address returned by the VirtualAlloc function when the region of pages is reserved.

Zitat

If the dwFreeType parameter is MEM_RELEASE, this parameter must be 0 (zero). The function frees the entire region that is reserved in the initial allocation call to VirtualAlloc.
Meh... Mit ::VirtualAlloc werde ich die Speicher-Verschwendung also nicht los.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Evrey« (05.06.2014, 20:10)


19

05.06.2014, 20:10

Die einzige Lösung, von der ich jetzt wüsste, wäre das hier:

[WARNUNG! Pseudo-Code voraus!!!]

Quellcode

1
2
3
4
5
6
7
8
function allocate_aligned(alignment)
    ASSERT(alignment <= 256 && alignment > 1);
    max_size = size_in_bytes + alignment;
    raw_ptr = allocate_unaligned(max_size);
    offset = /* Diesen Part lasse ich von dir ausfüllen. */
    *((unsigned char*)(raw_ptr)) = offset;
    aligned_address = (char*)(raw_ptr) + 1;
    return aligned_address;


Was dieser Schnipsel hier effektiv macht ist folgendes: Er berechnet die maximale Größe, die eine Addresserweiterung mit sich bringen kann, und alloziiert Speicher dieser Größe. Dann berechnet er das Offset, um das die Addresse nach oben reguliert werden muss, damit sie "aligned" ist. Da alignment immer größer als 1 Byte aber nie größer als 256 Bytes sein darf, können wir in das erste byte des zurückgegebenen raw_ptrs das genutzte offset schreiben, damit wir später aus der "aligned" Addresse die rohe Addresse wieder berechnen können. Da in ein Byte nur 256 Zahlen aufnehmen kann, können wir auch höchstens ein Offset von 256 Bytes speichern. Um den Speicher wieder freizugeben wird einfach die Basis-Adresse berechnet, und selbige dann an delete und co. übergeben.

Dieser Code ist nicht getestet, ich leiste also keine Garantie dafür. Ich habe allerdings selbst einmal eine solche Strategie in einem PoolAllocator umgesetzt, und habe dort auch nahezu diesen Trick verwendet. Ich hoffe, dass ich es einigermaßen gut erklärt habe, und natürlich, dass es deine Frage beantwortet. Der einzige Nachteil ist nun einmal, dass immer ein gewisser Overhead dazukommt. Eine bessere Strategie wüsste ich aber ehrlich gesagt auch nicht, insbesondere dann nicht, wenn die nativen Funktionen nicht zu funktionieren scheinen.

Liebe Grüße,
~ EuadeLuxe ~

PostScriptum:
Da deine Frage wohl eben ist, wie man an das Offset gelangt, gebe ich dir doch die Lösung *):
[...] bloß stellt sich die Frage, wie ich an dieses Offset gelange. Es könnte jedes Vielfache der Page-Größe des Betriebssystems sein, völlig willkürlich. [...]

[Pseudo-Code]

Quellcode

1
2
raw_address = (passender UInt type, entweder 32- oder 64-bit)raw_ptr;
offset = (alignment - (raw_address & (alignment - 1)));

Bitte tu dir aber den Gefallen, und denke einmal darüber nach, wieso man das Offset so berechnen kann.

*) Alle Angaben ohne Gewähr. Das mag blöd klingen, aber es kann natürlich durchaus sein, dass ich einen Fehler mache.

Evrey

Treue Seele

  • »Evrey« ist der Autor dieses Themas

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

20

05.06.2014, 20:50

Danke für die ausführliche Antwort. °^°
Allerdings hat sie leider nicht wirklich weiter geholfen. Deine Allokierungs-Funktion macht genau das, was auch _aligned_malloc tut: Extra-Speicher für Alignment verschwenden, was bei der beachtlichen Größe wie von mir gewünscht nicht mehr ganz so knorke erscheint. Das rentiert sich erst, wenn man mehrere Arenas auf einmal allokiert, so dass der Anteil an ungenutztem Speicher fix schrumpft.

Zum Algorithmus im Nachtrag:
Nehmen wir mal ein ganz simples Mini-Beispiel. Eine Arena sei 16 Bytes groß und eine Zelle 1 Byte. Die Pages des Betriebssystems seien 2 Bytes groß. Die durch z.B. ::VirtualAlloc erhaltene Basis-Adresse der Arena A ist 0x12. Das Offset um korrektes Alignment vorzutäuschen wäre also 0x02. Nehmen wir mal an, dass ein Objekt a an der Adresse 0x15 allokiert wurde. Der Algorithmus muss jetzt a:0x15 auf A:0x12 mappen. Das geht fix: A = (a - 0x02) & -16
Nun nehmen wir eine zweite Arena B:0x26 und ein Objekt b:0x31. Jetzt ist das Offset 0x05. Jetzt haben wir eine etwas andere Korrektur-Funktion: B = (b - 0x05) & -16
Das Blöde ist jetzt, dass ich aus keinem der Objekt-Pointer eindeutig die Offset-Informationen herausziehen kann, wo genau ihre Arenas anfangen. Dein kleiner Algorithmus sagt bloß, wie ich von misaligned nach aligned komme, und das ist nur für die Implementierung genau solcher Funktionen interessant, wie oben bemängelt.


Edit:
Da Recherche nach Recherche nichts ergab, grübelte ich immer mehr an Möglichkeiten, dieses Leck zu kompensieren, oder eben gar zu meinen Gunsten zu nutzen. Und mir schwebt tatsächlich eine nicht uninteressante Idee vor, die ein bis zwei riesigen Lecks zu nutzen. Viele Objekte wie Strings und Listen brauchen eben zusätzliche Speicher-Blöcke, falls ihre Daten nicht mehr in die Zellen der Objekt-Arena passen. Dazu habe ich eine zweite Art von Arenen vorgesehen, die lediglich für solche Extra-Daten da sind und entsprechend keine so hohen Effizienz-Kriterien für den GC erfüllen müssen.
Wenn ich an meine Ruby-Zeit zurück denke, so fällt mir ein, dass z.B. die meisten irgendwo in einer Größenordnung liegen, dass sie in der Objekt-Arena inlined werden können, oder nur wenig zu groß für die Zellen sind. Anstatt sofort auf eine Daten-Arena zurück zu greifen, könnte ich also zwei Mini-Daten-Arenen auf die ein bis zwei riesigen Leaks verteilen, die ausschließlich von Objekten der zugehörigen Objekt-Arena genutzt werden. Sind sie überfüllt, wird auf die "richtigen" Daten-Arenen zurückgegriffen. Ich muss mir noch überlegen, wie ich die Meta-Daten gestalte, dass sie am besten in eine halbe Page passen und variabel-große Blöcke verwalten, aber das würde das Problem recht elegant lösen.
Keine Ahnung, warum mir diese Möglichkeit nicht früher in den Sinn kam.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »Evrey« (06.06.2014, 02:58)


Werbeanzeige