2013-08-13 2 views
10

Vor 15 Jahren habe ich beim Programmieren mit Pascal verstanden, warum man die Zweierpotenz für die Speicherzuweisung verwendet. Aber das scheint immer noch State-of-the-Art zu sein.Warum verwenden alle 2^n Nummern für die Zuweisung? -> new StringBuilder (256)

C# Beispiele:

new StringBuilder(256); 
new byte[1024]; 
int bufferSize = 1 << 12; 

Ich sehe noch diese tausende Male, verwende ich mir dies und ich bin in Frage noch:

Brauchen wir diese in modernen Programmiersprachen und moderne Hardware?
Ich schätze seine gute Praxis, aber was ist der Grund?

EDIT
Zum Beispiel eines byte[] Array, wie hier durch Antworten angegeben, wird eine Potenz von 2 keinen Sinn machen: (?) Das Array selbst 16 Byte verwenden wird, ist es so Sinn 240 zu bedienen (= 256-16) für die Größe von insgesamt 256 Bytes?

+0

Da die CPU Arbeit binär, Speicherblöcke (Seiten) sind 2^n Größe (warum Abfall Hälfte memblocks) und die sind zu indizieren einfach. Mehr Infos hier: http://en.wikipedia.org/wiki/Computer_number_format und hier: https://en.wikipedia.org/wiki/Page_(computer_memory) –

+1

Ich denke, die meisten ist zu zeigen, ... Sie können‘ t wirklich wissen, was genau Puffergröße für komplexe Klasse zugeordnet ('Dictionary' oder sogar' Liste 'oder' StringBuilder'), für Byte-Arrays etwas macht es Sinn, als Jack erklärt. –

Antwort

4

Brauchen wir diese in modernen Programmiersprachen und moderne Hardware? Ich schätze seine gute Praxis, aber was ist der Grund?

Es hängt davon ab. Es gibt zwei Dinge hier zu betrachten:

  1. Für die Größen kleiner als die Speicherseitengröße, gibt es keinen nennenswerten Unterschied zwischen einer Potenz von zwei und einer beliebigen Anzahl an Speicherplatz zuweisen;
  2. Sie verwenden meist verwaltete Datenstrukturen mit C#, so dass Sie nicht einmal wissen, wie viele Bytes tatsächlich darunter liegen.

Angenommen, Sie Low-Level-Zuweisung mit malloc() tun, ein Vielfaches von der Seitengröße wäre eine gute Idee, das heißt 4096 oder 8192 in Betracht gezogen werden; Dies liegt an der effizienteren Speicherverwaltung.

Mein Rat wäre, nur zuzuteilen, was Sie brauchen, und C# die Speicherverwaltung und -zuweisung für Sie behandeln lassen.

+1

Seitengröße ist irrelevant. Sie vernachlässigen die Zuordnung von Unterzuweisungs- und Speicherblockmetadaten. –

+0

@DavidHeffernan Umso mehr Gründe, sich überhaupt nicht mit 2^n Größen zu beschäftigen, ist das, was du sagst? –

+0

Ich sage, dass Ihr letzter Absatz falsch ist. –

0

IMHO, alles endet in der genauen Leistung der arithmetischen Operation von zwei ist wie eine schnelle Spur. Eine arithmetische Operation mit niedrigem Pegel für die Zweierpotenz benötigt weniger Anzahl von Umdrehungen und Bitmanipulationen als jede andere Zahl, die zusätzliche Arbeit für die CPU benötigt.

Und fand dies möglich Duplikat: Is it better to allocate memory in the power of two?

+0

1. Potenz von zwei wird normalerweise zur Kompilierzeit ausgewertet. 2. Compiler können das Verschieben optimieren, wenn es tatsächlich schneller ist. 3. Speicherzuordnung um Größenordnungen teurer als Arithmetik, so dass diese sogenannte Effizienz nie gemessen werden könnte. –

+0

basierend auf dem, was Sie sagen, es könnte wenig Aufwand für den Compiler, aber nie zur Laufzeit. Ich denke, das gilt für bestimmte Sprache, sondern nur für diese anspruchsvolle Compiler. Ich denke, Ihre Antwort ist genau dieser Fall, da dieser Thread für C# –

1

Es ist immer noch Sinn in bestimmten Fällen macht, aber ich würde es vorziehen, von Fall zu Fall zu prüfen, ob ich diese Art von Spezifikation brauche oder nicht, anstatt blind gut wie Praxis verwenden.

Zum Beispiel könnte es Fälle geben, in denen Sie genau 8 Bit Informationen (1 Byte) verwenden möchten, um eine Tabelle zu adressieren.

In diesem Fall würde ich die Tabelle die Größe 2^8 haben.

Object table = new Object[256]; 

Dadurch erhalten Sie nur eine byte mit einem beliebigen Objekt der Tabelle adressieren können.

Auch wenn die Tabelle tatsächlich kleiner ist und nicht alle 256 Stellen verwendet, haben Sie immer noch die Garantie für bidirektionales Mapping von Tabelle zu Index und von Index zu Tabelle, was Fehler verhindern könnte, die z Sie hatten:

Object table = new Object[100]; 

Und dann jemand (wahrscheinlich jemand anderes) greift es mit einem Byte-Wert außerhalb der Tabelle Bereich.

Vielleicht könnte diese Art von bijective Verhalten gut sein, vielleicht könnten Sie andere Möglichkeiten haben Ihre Zwänge zu gewährleisten.

Wahrscheinlich, angesichts der Zunahme der Intelligenz der aktuellen Compiler, ist es nicht die einzige gute Praxis mehr.

2

Leider ist es ziemlich dumm, wenn Sie einen Speicherblock in einer einzigen Speicherseite von 4k halten wollen ... Und Personen wissen, dass es nicht einmal :-) (Ich habe erst vor 10 Minuten .. . ich hatte nur eine Vermutung) ... ein Beispiel ... Es unsicheren Code und Implementierung abhängig ist (.NET 4.5 bei 32/64 Bit)

byte[] arr = new byte[4096]; 

fixed (byte* p = arr) 
{ 
    int size = ((int*)p)[IntPtr.Size == 4 ? -1 : -2]; 
} 

So ist die CLR zugewiesen hat mindestens 4096 + (1 mit oder 2) sizeof (int) ... Also ist es über eine 4k Speicherseite gegangen. Das ist logisch ... Es muss die Größe des Arrays irgendwo behalten, und es zusammen mit dem Array zu halten ist die intelligenteste Sache (für diejenigen, die wissen, was Pascal Strings und BSTR sind, ist es das gleiche Prinzip)

Ich werde hinzufügen, dass alle Objekte in .NET haben eine syncblck Nummer und eine RuntimeType ... Sie sind mindestens int wenn nicht IntPtr, so insgesamt zwischen 8 und 16 Bytes/Objekt (Dies wird an verschiedenen Stellen erläutert ... versuchen Sie nach .net object header wenn Sie interessiert sind)

0

Ja, es ist gute Praxis, und es hat mindestens einen Grund. Die modernen Prozessoren haben L1-Cache-Zeilengröße 64 Bytes, und wenn Sie Puffergröße als 2^n verwenden (zB 1024, 4096, ..), werden Sie vollständig Cache-Zeile, ohne Platzverschwendung. In einigen Fällen wird dadurch das Problem der falschen Freigabe verhindert (http://en.wikipedia.org/wiki/False_sharing).