Jawaban:
Array array (jagged array) lebih cepat daripada array multi-dimensi dan dapat digunakan lebih efektif. Array multidimensi memiliki sintaks yang lebih bagus.
Jika Anda menulis beberapa kode sederhana menggunakan array bergerigi dan multidimensi dan kemudian memeriksa rakitan yang dikompilasi dengan disassembler IL, Anda akan melihat bahwa penyimpanan dan pengambilan dari array bergerigi (atau dimensi tunggal) adalah instruksi IL sederhana sedangkan operasi yang sama untuk array multidimensi adalah metode doa yang selalu lebih lambat.
Pertimbangkan metode berikut:
static void SetElementAt(int[][] array, int i, int j, int value)
{
array[i][j] = value;
}
static void SetElementAt(int[,] array, int i, int j, int value)
{
array[i, j] = value;
}
IL mereka adalah sebagai berikut:
.method private hidebysig static void SetElementAt(int32[][] 'array',
int32 i,
int32 j,
int32 'value') cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: ldelem.ref
IL_0003: ldarg.2
IL_0004: ldarg.3
IL_0005: stelem.i4
IL_0006: ret
} // end of method Program::SetElementAt
.method private hidebysig static void SetElementAt(int32[0...,0...] 'array',
int32 i,
int32 j,
int32 'value') cil managed
{
// Code size 10 (0xa)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: ldarg.3
IL_0004: call instance void int32[0...,0...]::Set(int32,
int32,
int32)
IL_0009: ret
} // end of method Program::SetElementAt
Saat menggunakan array bergerigi Anda dapat dengan mudah melakukan operasi seperti pertukaran baris dan pengubahan ukuran baris. Mungkin dalam beberapa kasus penggunaan array multidimensi akan lebih aman, tetapi bahkan Microsoft FxCop mengatakan bahwa array bergerigi harus digunakan daripada multidimensi ketika Anda menggunakannya untuk menganalisis proyek Anda.
Array multidimensi menciptakan tata letak memori linier yang bagus sementara array bergerigi menyiratkan beberapa tingkat tipuan tambahan.
Mencari nilai jagged[3][6]
dalam array bergerigivar jagged = new int[10][5]
berfungsi seperti ini: Cari elemen pada indeks 3 (yang merupakan array) dan cari elemen pada indeks 6 dalam array itu (yang merupakan nilai). Untuk setiap dimensi dalam kasus ini, ada tampilan tambahan (ini adalah pola akses memori yang mahal).
Array multidimensi diletakkan secara linear dalam memori, nilai aktual ditemukan dengan mengalikan indeks-indeks tersebut. Namun, mengingat array var mult = new int[10,30]
, Length
properti array multidimensi mengembalikan jumlah elemen yaitu 10 * 30 = 300.
The Rank
properti dari array bergerigi selalu 1, tapi array multidimensi dapat memiliki peringkat apapun. The GetLength
Metode array apapun dapat digunakan untuk mendapatkan panjang masing-masing dimensi. Untuk array multidimensi dalam contoh ini mult.GetLength(1)
mengembalikan 30.
Mengindeks array multidimensi lebih cepat. misalkan diberi array multidimensi dalam contoh ini mult[1,7]
= 30 * 1 + 7 = 37, dapatkan elemen pada indeks 37. Ini adalah pola akses memori yang lebih baik karena hanya satu lokasi memori yang terlibat, yang merupakan alamat dasar dari array.
Oleh karena itu, array multidimensi mengalokasikan blok memori kontinu, sedangkan array bergerigi tidak harus persegi, misalnya jagged[1].Length
tidak harus sama denganjagged[2].Length
, yang akan berlaku untuk array multidimensi apa pun.
Dari segi kinerja, array multidimensi harus lebih cepat. Jauh lebih cepat, tetapi karena implementasi CLR yang sangat buruk mereka tidak.
23.084 16.634 15.215 15.489 14.407 13.691 14.695 14.398 14.551 14.252
25.782 27.484 25.711 20.844 19.607 20.349 25.861 26.214 19.677 20.171
5.050 5.085 6.412 5.225 5.100 5.751 6.650 5.222 6.770 5.305
Baris pertama adalah pengaturan array bergerigi, baris kedua menunjukkan array multidimensi dan baris ketiga, yah begitulah seharusnya. Program ditunjukkan di bawah ini, FYI ini diuji menjalankan mono. (Pengaturan waktu windows sangat berbeda, kebanyakan karena variasi implementasi CLR).
Di windows, timing array bergerigi sangat unggul, hampir sama dengan interpretasi saya sendiri tentang seperti apa seharusnya array multidimensi, lihat 'Tunggal ()'. Sayangnya windows JIT-compiler benar-benar bodoh, dan ini sayangnya membuat diskusi kinerja ini sulit, ada terlalu banyak ketidakkonsistenan.
Ini adalah timing yang saya dapatkan di windows, kesepakatan yang sama di sini, baris pertama adalah array bergerigi, multidimensi kedua dan ketiga implementasi multidimensi saya sendiri, perhatikan berapa banyak ini lebih lambat di windows dibandingkan dengan mono.
8.438 2.004 8.439 4.362 4.936 4.533 4.751 4.776 4.635 5.864
7.414 13.196 11.940 11.832 11.675 11.811 11.812 12.964 11.885 11.751
11.355 10.788 10.527 10.541 10.745 10.723 10.651 10.930 10.639 10.595
Kode sumber:
using System;
using System.Diagnostics;
static class ArrayPref
{
const string Format = "{0,7:0.000} ";
static void Main()
{
Jagged();
Multi();
Single();
}
static void Jagged()
{
const int dim = 100;
for(var passes = 0; passes < 10; passes++)
{
var timer = new Stopwatch();
timer.Start();
var jagged = new int[dim][][];
for(var i = 0; i < dim; i++)
{
jagged[i] = new int[dim][];
for(var j = 0; j < dim; j++)
{
jagged[i][j] = new int[dim];
for(var k = 0; k < dim; k++)
{
jagged[i][j][k] = i * j * k;
}
}
}
timer.Stop();
Console.Write(Format,
(double)timer.ElapsedTicks/TimeSpan.TicksPerMillisecond);
}
Console.WriteLine();
}
static void Multi()
{
const int dim = 100;
for(var passes = 0; passes < 10; passes++)
{
var timer = new Stopwatch();
timer.Start();
var multi = new int[dim,dim,dim];
for(var i = 0; i < dim; i++)
{
for(var j = 0; j < dim; j++)
{
for(var k = 0; k < dim; k++)
{
multi[i,j,k] = i * j * k;
}
}
}
timer.Stop();
Console.Write(Format,
(double)timer.ElapsedTicks/TimeSpan.TicksPerMillisecond);
}
Console.WriteLine();
}
static void Single()
{
const int dim = 100;
for(var passes = 0; passes < 10; passes++)
{
var timer = new Stopwatch();
timer.Start();
var single = new int[dim*dim*dim];
for(var i = 0; i < dim; i++)
{
for(var j = 0; j < dim; j++)
{
for(var k = 0; k < dim; k++)
{
single[i*dim*dim+j*dim+k] = i * j * k;
}
}
}
timer.Stop();
Console.Write(Format,
(double)timer.ElapsedTicks/TimeSpan.TicksPerMillisecond);
}
Console.WriteLine();
}
}
Sederhananya array multidimensi mirip dengan tabel di DBMS.
Array of Array (jagged array) memungkinkan Anda membuat setiap elemen memiliki array lain dengan jenis panjang variabel yang sama.
Jadi, jika Anda yakin bahwa struktur data terlihat seperti tabel (baris tetap / kolom), Anda dapat menggunakan array multi dimensi. Array bergerigi adalah elemen tetap & setiap elemen dapat memiliki array dengan panjang variabel
Misalnya Psuedocode:
int[,] data = new int[2,2];
data[0,0] = 1;
data[0,1] = 2;
data[1,0] = 3;
data[1,1] = 4;
Pikirkan hal di atas sebagai tabel 2x2:
1 | 2 3 | 4
int[][] jagged = new int[3][];
jagged[0] = new int[4] { 1, 2, 3, 4 };
jagged[1] = new int[2] { 11, 12 };
jagged[2] = new int[3] { 21, 22, 23 };
Pikirkan hal di atas karena setiap baris memiliki jumlah variabel kolom:
1 | 2 | 3 | 4 11 | 12 21 | 22 | 23
Pendahuluan: Komentar ini ditujukan untuk menjawab jawaban yang diberikan oleh okutane , tetapi karena sistem reputasi konyol SO, saya tidak dapat memposting di tempat yang seharusnya.
Pernyataan Anda bahwa yang satu lebih lambat daripada yang lain karena panggilan metode tidak benar. Satu lebih lambat dari yang lain karena algoritma pemeriksaan batas yang lebih rumit. Anda dapat dengan mudah memverifikasi ini dengan melihat, bukan pada IL, tetapi pada majelis yang dikompilasi. Sebagai contoh, pada 4,5 instal saya, mengakses elemen (melalui pointer di edx) disimpan dalam array dua dimensi yang ditunjukkan oleh ecx dengan indeks yang disimpan dalam eax dan edx terlihat seperti ini:
sub eax,[ecx+10]
cmp eax,[ecx+08]
jae oops //jump to throw out of bounds exception
sub edx,[ecx+14]
cmp edx,[ecx+0C]
jae oops //jump to throw out of bounds exception
imul eax,[ecx+0C]
add eax,edx
lea edx,[ecx+eax*4+18]
Di sini, Anda dapat melihat bahwa tidak ada overhead dari panggilan metode. Pengecekan batas sangat berbelit-belit berkat kemungkinan indeks yang tidak nol, yang merupakan fungsi yang tidak ditawarkan dengan array bergerigi. Jika kita menghapus sub, cmp, dan jmps untuk case-case yang bukan nol, kode ini cukup banyak untuk dipecahkan (x*y_max+y)*sizeof(ptr)+sizeof(array_header)
. Penghitungan ini kira-kira sama cepat (satu kali lipat dapat diganti dengan shift, karena itulah alasan utama kami memilih byte untuk diukur sebagai kekuatan dua bit) seperti yang lainnya untuk akses acak ke suatu elemen.
Komplikasi lain adalah bahwa ada banyak kasus di mana kompiler modern akan mengoptimalkan jauh memeriksa batas-batas untuk akses elemen sambil iterasi melalui array satu dimensi. Hasilnya adalah kode yang pada dasarnya hanya memajukan penunjuk indeks ke memori yang berdekatan dari array. Iterasi naif atas array multi-dimensi umumnya melibatkan lapisan tambahan dari logika bersarang, sehingga kompiler cenderung mengoptimalkan operasi. Jadi, meskipun overhead-memeriksa batas mengakses elemen tunggal diamortisasi ke runtime konstan sehubungan dengan dimensi dan ukuran array, kasus uji sederhana untuk mengukur perbedaan mungkin membutuhkan waktu beberapa kali lebih lama untuk dieksekusi.
Saya ingin memperbarui ini, karena dalam . Core Core multi-dimensi array lebih cepat daripada array bergerigi . Saya menjalankan tes dari John Leidegren dan ini adalah hasil pada .NET Core 2.0 preview 2. Saya meningkatkan nilai dimensi untuk membuat pengaruh yang mungkin dari aplikasi latar belakang kurang terlihat.
Debug (code optimalization disabled)
Running jagged
187.232 200.585 219.927 227.765 225.334 222.745 224.036 222.396 219.912 222.737
Running multi-dimensional
130.732 151.398 131.763 129.740 129.572 159.948 145.464 131.930 133.117 129.342
Running single-dimensional
91.153 145.657 111.974 96.436 100.015 97.640 94.581 139.658 108.326 92.931
Release (code optimalization enabled)
Running jagged
108.503 95.409 128.187 121.877 119.295 118.201 102.321 116.393 125.499 116.459
Running multi-dimensional
62.292 60.627 60.611 60.883 61.167 60.923 62.083 60.932 61.444 62.974
Running single-dimensional
34.974 33.901 34.088 34.659 34.064 34.735 34.919 34.694 35.006 34.796
Saya melihat ke pembongkaran dan ini adalah apa yang saya temukan
jagged[i][j][k] = i * j * k;
dibutuhkan 34 instruksi untuk dieksekusi
multi[i, j, k] = i * j * k;
diperlukan 11 instruksi untuk dieksekusi
single[i * dim * dim + j * dim + k] = i * j * k;
dibutuhkan 23 instruksi untuk dieksekusi
Saya tidak dapat mengidentifikasi mengapa array satu dimensi masih lebih cepat daripada multi-dimensi tetapi dugaan saya adalah bahwa itu ada hubungannya dengan beberapa optimasi yang dilakukan pada CPU.
Array multi-dimensi adalah (n-1) matriks -dimensi.
Begitu int[,] square = new int[2,2]
juga matriks persegi 2x2, int[,,] cube = new int [3,3,3]
adalah matriks kubus - persegi 3x3. Tidak perlu proporsionalitas.
Array bergerigi hanya array array - array di mana setiap sel berisi array.
Jadi MDA proporsional, JD mungkin tidak! Setiap sel dapat berisi array dengan panjang sewenang-wenang!
Selain jawaban lain, perhatikan bahwa array multidimensi dialokasikan sebagai satu objek tebal besar di heap. Ini memiliki beberapa implikasi:
<gcAllowVeryLargeObjects>
array multidimensi sebelum masalah akan pernah muncul jika Anda hanya menggunakan array bergerigi.Saya mem-parsing file .il yang dihasilkan oleh ildasm untuk membangun database assemnblies, kelas, metode, dan prosedur tersimpan untuk digunakan melakukan konversi. Saya menemukan yang berikut, yang mematahkan parsing saya.
.method private hidebysig instance uint32[0...,0...]
GenerateWorkingKey(uint8[] key,
bool forEncryption) cil managed
Buku Expert .NET 2.0 IL Assembler, oleh Serge Lidin, Apress, yang diterbitkan tahun 2006, Bab 8, Primitive Types and Signatures, hlm. 149-150 menjelaskan.
<type>[]
disebut sebagai Vektor dari <type>
,
<type>[<bounds> [<bounds>**] ]
disebut array <type>
**
berarti dapat diulang, [ ]
berarti opsional.
Contoh: Biarkan <type> = int32
.
1) int32[...,...]
adalah susunan dua dimensi dari batas bawah dan ukuran yang tidak ditentukan
2) int32[2...5]
adalah array satu dimensi dari batas bawah 2 dan ukuran 4.
3) int32[0...,0...]
adalah susunan dua dimensi dengan batas bawah 0 dan ukuran tidak terdefinisi.
Tom
double[,]
adalah array persegi panjang, sementaradouble[][]
dikenal sebagai "array bergerigi". Yang pertama akan memiliki jumlah "kolom" yang sama untuk setiap baris, sedangkan yang kedua akan (berpotensi) memiliki jumlah "kolom" yang berbeda untuk setiap baris.