Frage

Im Grunde haben Sie zwei Möglichkeiten, dies zu tun:

for (int x = 0; x < UPPER_X; x++)
    for (int y = 0; y < UPPER_Y; y++)
    {        
        arr1[x, y] = get_value();
        arr2[y, x] = get_value();
    }

Der einzige Unterschied ist, welche Variablen in der inneren Schleife zu ändern: erste oder zweite. Ich habe gehört, dass die Ergebnisse von Sprache zu Sprache unterscheiden.

Was ist richtig, um für .NET?

War es hilfreich?

Lösung

Der Grund ist schneller als der andere mit dem Cache des Prozessors zu tun hat, und die Art und Weise werden die Daten im Speicher angelegt.

Es gibt zwei normale Wege, um die zweidimensionalen Daten zu speichern ist in eindimensionalen Adressraum, Sie können entweder für die erste Zeile alle Daten speichern, dann die zweite Reihe, und so weiter (auch bekannt als Reihenhauptordnung ), oder Sie können es durch Spalten tun (auch bekannt als < a href = "http://en.wikipedia.org/wiki/Row-major_order#Column-major_order" rel = "nofollow noreferrer"> Spaltenhauptordnung ). Im Folgenden finden Sie, was die Speicherplätze für beide Optionen für eine 3x3-Array sein würde.

Zeilen:

1 2 3
4 5 6
7 8 9

Spalten:

1 4 7
2 5 8
3 6 9

Wenn Sie einen Speicherplatz zuzugreifen, eine gesamte Cache-Zeile (die zwischen 8 und 512 Bytes, entsprechend Wikipedia sein kann) wird in den Cache geladen. Wenn Sie also das nächste Speicherplatz zugreifen wird es bereits im Cache sein. So kann es viel schneller Speicher sequentiell zuzugreifen, als um den Raum in der Adresse zu springen. So mit großen zweidimensionalen Anordnungen, kann eine deutliche Geschwindigkeits Unterschied zwischen Zeilen oder Spalten als innere Schleife Variable wählen.

Andere Tipps

Sie sollten Benchmark Ihre besonderen Umständen sicher sein.

Sie würden denken, dass es kein Unterschied für rechteckigen Arrays wäre (dh zusammenhängend zugewiesenen Speicher), aber nach diesem MSDN-Artikel gibt es einen Unterschied:

  

können Sie erhalten noch bessere Ergebnisse durch   Umwandeln einer multidimensionalen Array   in ein eindimensionales Array. Wenn   Sie nicht die Syntax ausmacht, kann das sein   trivial; verwenden nur einen Index als   versetzt. Zum Beispiel, nach dem   deklariert ein eindimensionales Array   als zweidimensionale Anordnung verwendet werden:

double[] myArray = new double[ROW_DIM * COLUMN_DIM];
  

Für die Indizierung der Elemente dieser   Array, verwenden Sie die folgende Offset:

myArray[row * COLUMN_DIM + column];
  

Dies wird zweifellos schneller als   ein äquivalenter gezackten oder rechteckigen   Array.

So habe ich die Benchmark und die Ergebnisse sind, dass der Zugang zu arr1 dreimal schneller ist.

Sehr interessant, ich habe nie aufgehört zu bedenken, dass einfach einen so großen Unterschied, indem sie den Array-Indizes „nicht sequentiell“ zugreifen. Ich habe ein es versuchen, mich und fand auch der folgende Code die zweite Schleife hatte nehmen zwischen 2 - 3-mal länger:

// Hmmm, how to insert blank lines in the code formatter???
static void Main(string[] args)
{
    Stopwatch timer = new Stopwatch();
    int arraySize = 10000;

    // First array, access X by Y
    int[,] xbyy = new int[arraySize, arraySize];


    timer.Start();
    for (int x = 0; x < arraySize; x++)
        for (int y = 0; y < arraySize; y++)
        {
            xbyy[x, y] = 15;
        }
    timer.Stop();
    TimeSpan duration = timer.Elapsed;
    string realTimeFormat = string.Format("{0:00} minutes {1:00} seconds {2:000} milliseconds",
                    duration.Minutes, duration.Seconds, duration.Milliseconds);
    Console.WriteLine("X by Y took " + realTimeFormat);



    // Seecond array, access Y by X
    int[,] ybyx = new int[arraySize, arraySize];

    timer.Start();
    for (int x = 0; x < arraySize; x++)
        for (int y = 0; y < arraySize; y++)
        {
            ybyx[y, x] = 15;
        }
    timer.Stop();
    duration = timer.Elapsed;
    realTimeFormat = string.Format("{0:00} minutes {1:00} seconds {2:000} milliseconds",
                duration.Minutes, duration.Seconds, duration.Milliseconds);
    Console.WriteLine("Y by X took " + realTimeFormat);


    Console.ReadLine();
}

Um es kurz zu halten, ist hier der emittierten IL-Schnipsel für die X durch Y-Schleife und der Y durch X-Schleife.

Initial-Code vor dem Looping,

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       290 (0x122)
  .maxstack  4
  .locals init ([0] class [System]System.Diagnostics.Stopwatch timer,
           [1] int32 arraySize,
           [2] int32[0...,0...] xbyy,
           [3] int32 x,
           [4] int32 y,
           [5] valuetype [mscorlib]System.TimeSpan duration,
           [6] string realTimeFormat,
           [7] int32[0...,0...] ybyx,
           [8] int32 V_8,
           [9] int32 V_9)
  IL_0000:  newobj     instance void [System]System.Diagnostics.Stopwatch::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldc.i4     0x2710
  IL_000b:  stloc.1

Looping X durch Y

  IL_000c:  ldloc.1
  IL_000d:  ldloc.1
  IL_000e:  newobj     instance void int32[0...,0...]::.ctor(int32,
                                                             int32)
  IL_0013:  stloc.2
  IL_0014:  ldloc.0
  IL_0015:  callvirt   instance void [System]System.Diagnostics.Stopwatch::Start()
  IL_001a:  ldc.i4.0
  IL_001b:  stloc.3
  IL_001c:  br.s       IL_003d
  IL_001e:  ldc.i4.0
  IL_001f:  stloc.s    y
  IL_0021:  br.s       IL_0034
  IL_0023:  ldloc.2
  IL_0024:  ldloc.3
  IL_0025:  ldloc.s    y
  IL_0027:  ldc.i4.s   15
  IL_0029:  call       instance void int32[0...,0...]::Set(int32,
                                                           int32,
                                                           int32)
  IL_002e:  ldloc.s    y
  IL_0030:  ldc.i4.1
  IL_0031:  add
  IL_0032:  stloc.s    y
  IL_0034:  ldloc.s    y
  IL_0036:  ldloc.1
  IL_0037:  blt.s      IL_0023
  IL_0039:  ldloc.3
  IL_003a:  ldc.i4.1
  IL_003b:  add
  IL_003c:  stloc.3
  IL_003d:  ldloc.3
  IL_003e:  ldloc.1
  IL_003f:  blt.s      IL_001e
  IL_0041:  ldloc.0

Looping Y durch X

  IL_0090:  ldloc.1
  IL_0091:  ldloc.1
  IL_0092:  newobj     instance void int32[0...,0...]::.ctor(int32,
                                                             int32)
  IL_0097:  stloc.s    ybyx
  IL_0099:  ldloc.0
  IL_009a:  callvirt   instance void [System]System.Diagnostics.Stopwatch::Start()
  IL_009f:  ldc.i4.0
  IL_00a0:  stloc.s    V_8
  IL_00a2:  br.s       IL_00c7
  IL_00a4:  ldc.i4.0
  IL_00a5:  stloc.s    V_9
  IL_00a7:  br.s       IL_00bc
  IL_00a9:  ldloc.s    ybyx
  IL_00ab:  ldloc.s    V_9
  IL_00ad:  ldloc.s    V_8
  IL_00af:  ldc.i4.s   15
  IL_00b1:  call       instance void int32[0...,0...]::Set(int32,
                                                           int32,
                                                           int32)
  IL_00b6:  ldloc.s    V_9
  IL_00b8:  ldc.i4.1
  IL_00b9:  add
  IL_00ba:  stloc.s    V_9
  IL_00bc:  ldloc.s    V_9
  IL_00be:  ldloc.1
  IL_00bf:  blt.s      IL_00a9
  IL_00c1:  ldloc.s    V_8
  IL_00c3:  ldc.i4.1
  IL_00c4:  add
  IL_00c5:  stloc.s    V_8
  IL_00c7:  ldloc.s    V_8
  IL_00c9:  ldloc.1
  IL_00ca:  blt.s      IL_00a4
  IL_00cc:  ldloc.0

Der IL logische Ablauf ist ähnlich. Der Hauptunterschied I beobachten kann, ist die erste Schleife stloc ldloc und für die Positionen 2 und 3 für die erste Anordnung und das X-Index-Variable zu verwenden, verwaltet. Durch die Zeit, um es in die zweite Schleife kam, hatte er die maxstack verbraucht und benutzt somit die stloc.s und ldloc.s Anweisungen. Ich glaube, dies ist der Unterschied Variablen auf dem Stack im Vergleich zu auf dem Heap zwischen Referenzierung, und die langsameren Leistung beitragen.

Nun, wenn Sie den Auftrag tauschen, in dem die Schleifen getestet werden, so dass der Y von X Schleife zuerst ausgeführt wird Stapel Referenzen zugreifen, sehen Sie die Zeitdauer umgekehrt gehen.


UPDATE: Ich war falsch über Referenzierung Stack oder Heap-Adressen. Es scheint nur, dass die ersten vier Variablen in einer Methode effizienter mit dem dedizierten stloc.0 zu referenzieren, 1, 3, 4 und ldloc.0, 1, 3, 4 OP-Codes.

http://weblogs.asp.net/mnolton /archive/2004/01/09/48992.aspx

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top