Apakah ada kecepatan analisis atau keunggulan penggunaan memori saat menggunakan HDF5 untuk penyimpanan array besar (bukan file biner datar)?


97

Saya sedang memproses array 3D besar, yang sering kali perlu saya potong dengan berbagai cara untuk melakukan berbagai analisis data. "Kubus" biasa bisa berukuran ~ 100 GB (dan kemungkinan akan bertambah besar di masa mendatang)

Tampaknya format file tipikal yang direkomendasikan untuk kumpulan data besar dengan python adalah menggunakan HDF5 (baik h5py atau pytables). Pertanyaan saya adalah: apakah ada manfaat kecepatan atau penggunaan memori untuk menggunakan HDF5 untuk menyimpan dan menganalisis kubus ini daripada menyimpannya dalam file biner datar sederhana? Apakah HDF5 lebih sesuai untuk data tabular, dibandingkan dengan array besar seperti yang saya kerjakan? Saya melihat bahwa HDF5 dapat memberikan kompresi yang bagus, tetapi saya lebih tertarik pada kecepatan pemrosesan dan menangani overflow memori.

Saya sering ingin menganalisis hanya satu bagian besar dari kubus. Salah satu kelemahan dari pytables dan h5py adalah bahwa ketika saya mengambil sepotong array, saya selalu mendapatkan kembali array yang numpy, menggunakan memori. Namun, jika saya mengiris memmap dari file biner datar, saya bisa mendapatkan tampilan, yang menyimpan data di disk. Jadi, tampaknya saya dapat lebih mudah menganalisis sektor tertentu dari data saya tanpa membebani memori saya.

Saya telah menjelajahi pytables dan h5py, dan sejauh ini belum melihat manfaat keduanya untuk tujuan saya.


1
HDF adalah format file "potongan". Rata-rata, ini akan memberi Anda pembacaan yang lebih cepat untuk potongan sembarang kumpulan data Anda. Sebuah memmap akan memiliki kasus terbaik yang cepat, tetapi kasus terburuk yang sangat, sangat lambat. h5pylebih cocok untuk kumpulan data seperti milik Anda daripada pytables. Juga, h5pytidak tidak kembali di memori array yang numpy. Sebaliknya ia mengembalikan sesuatu yang berperilaku seperti itu, tetapi tidak dimuat ke dalam memori (mirip dengan memmappedarray). Saya sedang menulis jawaban yang lebih lengkap (mungkin tidak menyelesaikannya), tapi semoga komentar ini sedikit membantu sementara itu.
Joe Kington

Terima kasih. Saya setuju bahwa h5py mengembalikan kumpulan data yang mirip dengan memmap. Tetapi, jika Anda melakukan potongan dari kumpulan data h5py, itu mengembalikan array numpy, yang saya percaya (?) Berarti data telah dimasukkan ke dalam memori tanpa perlu. Sebuah memmamp mengembalikan tampilan ke memmap asli jika memungkinkan. Dengan kata lain: type(cube)memberi h5py._hl.dataset.Dataset. Saat type(cube[0:1,:,:])memberi numpy.ndarray.
Kaleb

Namun, pendapat Anda tentang waktu membaca rata-rata itu menarik.
Kaleb

4
Jika Anda memiliki hambatan I / O maka dalam banyak kasus kompresi sebenarnya dapat meningkatkan kinerja baca / tulis (terutama menggunakan pustaka kompresi cepat seperti BLOSC dan LZO), karena ini mengurangi bandwidth I / O yang diperlukan dengan biaya beberapa siklus CPU tambahan . Anda mungkin ingin melihat halaman ini , yang memiliki banyak informasi tentang mengoptimalkan kinerja baca-tulis menggunakan file PyTables HDF5.
ali_m

2
"jika saya mengiris memmap numpy dari file biner datar, saya bisa mendapatkan tampilan, yang menyimpan data di disk" - itu mungkin benar, tetapi jika Anda benar-benar ingin melakukan apa pun dengan nilai dalam larik itu, cepat atau lambat Anda harus memuatnya ke dalam RAM. Sebuah array yang dipetakan memori hanya menyediakan beberapa enkapsulasi sehingga Anda tidak perlu memikirkan secara tepat kapan data dibaca atau apakah itu akan melebihi kapasitas memori sistem Anda. Dalam beberapa keadaan, perilaku caching asli dari larik memmaped memang bisa sangat suboptimal .
ali_m

Jawaban:


162

Keunggulan HDF5: Organisasi, fleksibilitas, interoperabilitas

Beberapa keunggulan utama HDF5 adalah struktur hierarkinya (mirip dengan folder / file), metadata arbitrer opsional yang disimpan dengan setiap item, dan fleksibilitasnya (misalnya kompresi). Struktur organisasi dan penyimpanan metadata ini mungkin terdengar sepele, tetapi sangat berguna dalam praktiknya.

Keuntungan lain dari HDF adalah bahwa kumpulan data dapat berukuran tetap atau berukuran fleksibel. Oleh karena itu, mudah untuk menambahkan data ke kumpulan data besar tanpa harus membuat salinan baru seluruhnya.

Selain itu, HDF5 adalah format standar dengan pustaka yang tersedia untuk hampir semua bahasa, jadi berbagi data di disk Anda antara, katakanlah Matlab, Fortran, R, C, dan Python sangat mudah dengan HDF. (Agar adil, tidak terlalu sulit dengan array biner yang besar, juga, selama Anda mengetahui urutan C vs. F dan mengetahui bentuk, tipe d, dll dari array yang disimpan.)

Keunggulan HDF untuk array besar: I / O lebih cepat dari slice arbitrer

Sama seperti TL / DR: Untuk array 3D ~ 8 GB, membaca potongan "penuh" di sepanjang sumbu apa pun membutuhkan waktu ~ 20 detik dengan kumpulan data HDF5 yang terpotong, dan 0,3 detik (kasus terbaik) hingga lebih dari tiga jam (kasus terburuk) untuk array yang dipetakan dari data yang sama.

Di luar hal-hal yang tercantum di atas, ada keuntungan besar lain dari format data pada disk yang "terpotong" * seperti HDF5: Membaca potongan sembarang (penekanan pada sembarang) biasanya akan jauh lebih cepat, karena data pada disk lebih berdekatan rata-rata.

*(HDF5 tidak harus dalam format data chunked. Ini mendukung chunking, tetapi tidak memerlukannya. Faktanya, default untuk membuat dataset di h5pybukanlah chunk, jika saya ingat dengan benar.)

Pada dasarnya, kecepatan pembacaan disk kasus terbaik Anda dan kecepatan pembacaan disk kasus terburuk untuk bagian tertentu dari kumpulan data Anda akan cukup dekat dengan kumpulan data HDF yang dipotong (dengan asumsi Anda memilih ukuran potongan yang wajar atau membiarkan perpustakaan memilih satu untuk Anda). Dengan array biner sederhana, kasus terbaik lebih cepat, tetapi kasus terburuk jauh lebih buruk.

Satu peringatan, jika Anda memiliki SSD, Anda kemungkinan tidak akan melihat perbedaan besar dalam kecepatan baca / tulis. Dengan hard drive biasa, pembacaan berurutan jauh lebih cepat daripada pembacaan acak. (mis. hard drive biasa memiliki seekwaktu lama .) HDF masih memiliki keunggulan pada SSD, tetapi lebih karena fitur-fiturnya yang lain (misalnya metadata, organisasi, dll) daripada karena kecepatan mentah.


Pertama, untuk menghilangkan kebingungan, mengakses h5pyset data akan mengembalikan objek yang berperilaku cukup mirip dengan array numpy, tetapi tidak memuat data ke dalam memori hingga diiris. (Mirip dengan memmap, tetapi tidak identik.) Lihat h5pypengantar untuk informasi lebih lanjut.

Mengiris dataset akan memuat subset data ke dalam memori, tetapi mungkin Anda ingin melakukan sesuatu dengannya, pada titik mana Anda tetap membutuhkannya di memori.

Jika Anda ingin melakukan penghitungan out-of-core, Anda dapat dengan mudah menggunakan data tabel dengan pandasatau pytables. Hal ini dimungkinkan dengan h5py(lebih bagus untuk array ND besar), tetapi Anda perlu turun ke tingkat yang lebih rendah dan menangani iterasi sendiri.

Namun, masa depan komputasi out-of-core numpy-like adalah Blaze. Silakan lihat jika Anda benar-benar ingin mengambil rute itu.


Kasus "belum dipotong"

Pertama, pertimbangkan array berurutan C 3D yang ditulis ke disk (saya akan mensimulasikannya dengan memanggil arr.ravel()dan mencetak hasilnya, untuk membuatnya lebih terlihat):

In [1]: import numpy as np

In [2]: arr = np.arange(4*6*6).reshape(4,6,6)

In [3]: arr
Out[3]:
array([[[  0,   1,   2,   3,   4,   5],
        [  6,   7,   8,   9,  10,  11],
        [ 12,  13,  14,  15,  16,  17],
        [ 18,  19,  20,  21,  22,  23],
        [ 24,  25,  26,  27,  28,  29],
        [ 30,  31,  32,  33,  34,  35]],

       [[ 36,  37,  38,  39,  40,  41],
        [ 42,  43,  44,  45,  46,  47],
        [ 48,  49,  50,  51,  52,  53],
        [ 54,  55,  56,  57,  58,  59],
        [ 60,  61,  62,  63,  64,  65],
        [ 66,  67,  68,  69,  70,  71]],

       [[ 72,  73,  74,  75,  76,  77],
        [ 78,  79,  80,  81,  82,  83],
        [ 84,  85,  86,  87,  88,  89],
        [ 90,  91,  92,  93,  94,  95],
        [ 96,  97,  98,  99, 100, 101],
        [102, 103, 104, 105, 106, 107]],

       [[108, 109, 110, 111, 112, 113],
        [114, 115, 116, 117, 118, 119],
        [120, 121, 122, 123, 124, 125],
        [126, 127, 128, 129, 130, 131],
        [132, 133, 134, 135, 136, 137],
        [138, 139, 140, 141, 142, 143]]])

Nilai akan disimpan di disk secara berurutan seperti yang ditunjukkan pada baris 4 di bawah ini. (Mari kita abaikan detail sistem file dan fragmentasi untuk saat ini.)

In [4]: arr.ravel(order='C')
Out[4]:
array([  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,
        13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,
        26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
        39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
        52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,
        65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,
        78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,
        91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103,
       104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
       117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
       130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143])

Dalam skenario kasus terbaik, mari kita ambil potongan di sepanjang sumbu pertama. Perhatikan bahwa ini hanyalah 36 nilai pertama dari larik. Ini akan menjadi bacaan yang sangat cepat! (satu pencarian, satu bacaan)

In [5]: arr[0,:,:]
Out[5]:
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

Demikian pula, potongan berikutnya di sepanjang sumbu pertama hanya akan menjadi 36 nilai berikutnya. Untuk membaca potongan lengkap sepanjang sumbu ini, kita hanya membutuhkan satu seekoperasi. Jika semua yang akan kita baca adalah berbagai irisan di sepanjang sumbu ini, maka ini adalah struktur file yang sempurna.

Namun, mari pertimbangkan skenario terburuk: Sebuah potongan di sepanjang sumbu terakhir.

In [6]: arr[:,:,0]
Out[6]:
array([[  0,   6,  12,  18,  24,  30],
       [ 36,  42,  48,  54,  60,  66],
       [ 72,  78,  84,  90,  96, 102],
       [108, 114, 120, 126, 132, 138]])

Untuk membaca bagian ini, kita membutuhkan 36 pencarian dan 36 pembacaan, karena semua nilai dipisahkan pada disk. Tidak ada satupun yang berdekatan!

Ini mungkin tampak sangat kecil, tetapi saat kita mendapatkan array yang semakin besar, jumlah dan ukuran seekoperasi tumbuh dengan cepat. Untuk larik 3D berukuran besar (~ 10 Gb) yang disimpan dengan cara ini dan dibaca melalui memmap, membaca potongan penuh di sepanjang sumbu "terburuk" dapat memakan waktu puluhan menit dengan mudah, bahkan dengan perangkat keras modern. Pada saat yang sama, irisan di sepanjang sumbu terbaik dapat memakan waktu kurang dari satu detik. Untuk kesederhanaan, saya hanya menampilkan irisan "penuh" di sepanjang sumbu tunggal, tetapi hal yang sama persis terjadi dengan irisan sembarang subset data.

Kebetulan ada beberapa format file yang memanfaatkan ini dan pada dasarnya menyimpan tiga salinan array 3D besar pada disk: satu dalam urutan-C, satu dalam urutan-F, dan satu lagi di antara keduanya. (Contoh dari ini adalah format D3D Geoprobe, meskipun saya tidak yakin itu didokumentasikan di mana pun.) Siapa yang peduli jika ukuran file akhirnya adalah 4TB, penyimpanan itu murah! Hal gila tentang itu adalah karena kasus penggunaan utama mengekstrak satu sub-irisan di setiap arah, pembacaan yang ingin Anda buat sangat, sangat cepat. Ini bekerja dengan sangat baik!


Kasus sederhana yang "dipotong"

Misalkan kita menyimpan "potongan" 2x2x2 dari larik 3D sebagai blok yang berdekatan pada disk. Dengan kata lain, sesuatu seperti:

nx, ny, nz = arr.shape
slices = []
for i in range(0, nx, 2):
    for j in range(0, ny, 2):
        for k in range(0, nz, 2):
            slices.append((slice(i, i+2), slice(j, j+2), slice(k, k+2)))

chunked = np.hstack([arr[chunk].ravel() for chunk in slices])

Jadi data di disk akan terlihat seperti chunked:

array([  0,   1,   6,   7,  36,  37,  42,  43,   2,   3,   8,   9,  38,
        39,  44,  45,   4,   5,  10,  11,  40,  41,  46,  47,  12,  13,
        18,  19,  48,  49,  54,  55,  14,  15,  20,  21,  50,  51,  56,
        57,  16,  17,  22,  23,  52,  53,  58,  59,  24,  25,  30,  31,
        60,  61,  66,  67,  26,  27,  32,  33,  62,  63,  68,  69,  28,
        29,  34,  35,  64,  65,  70,  71,  72,  73,  78,  79, 108, 109,
       114, 115,  74,  75,  80,  81, 110, 111, 116, 117,  76,  77,  82,
        83, 112, 113, 118, 119,  84,  85,  90,  91, 120, 121, 126, 127,
        86,  87,  92,  93, 122, 123, 128, 129,  88,  89,  94,  95, 124,
       125, 130, 131,  96,  97, 102, 103, 132, 133, 138, 139,  98,  99,
       104, 105, 134, 135, 140, 141, 100, 101, 106, 107, 136, 137, 142, 143])

Dan hanya untuk menunjukkan bahwa itu adalah blok 2x2x2 arr, perhatikan bahwa ini adalah 8 nilai pertama dari chunked:

In [9]: arr[:2, :2, :2]
Out[9]:
array([[[ 0,  1],
        [ 6,  7]],

       [[36, 37],
        [42, 43]]])

Untuk membaca dalam potongan mana pun di sepanjang sumbu, kami akan membaca 6 atau 9 bagian yang berdekatan (dua kali lebih banyak data yang kami butuhkan) dan kemudian hanya menyimpan bagian yang kami inginkan. Itu kasus terburuk maksimum 9 pencarian vs maksimum 36 pencarian untuk versi non-chunked. (Tapi kasus terbaik masih 6 pencarian vs 1 untuk array yang dipetakan.) Karena pembacaan berurutan sangat cepat dibandingkan dengan pencarian, ini secara signifikan mengurangi jumlah waktu yang diperlukan untuk membaca subset arbitrer ke dalam memori. Sekali lagi, efek ini menjadi lebih besar dengan array yang lebih besar.

HDF5 mengambil langkah ini lebih jauh. Potongan tidak harus disimpan berdekatan, dan mereka diindeks oleh B-Tree. Selain itu, ukurannya tidak harus sama di disk, jadi kompresi dapat diterapkan ke setiap bagian.


Larik terpotong dengan h5py

Secara default, h5pytidak membuat file HDF yang dipotong pada disk (menurut saya pytables, sebaliknya). Namun, jika Anda menentukan chunks=Truesaat membuat kumpulan data, Anda akan mendapatkan larik terpotong pada disk.

Sebagai contoh cepat dan minimal:

import numpy as np
import h5py

data = np.random.random((100, 100, 100))

with h5py.File('test.hdf', 'w') as outfile:
    dset = outfile.create_dataset('a_descriptive_name', data=data, chunks=True)
    dset.attrs['some key'] = 'Did you want some metadata?'

Perhatikan bahwa chunks=Truememberitahu h5pyuntuk secara otomatis memilih ukuran potongan untuk kita. Jika Anda mengetahui lebih banyak tentang kasus penggunaan Anda yang paling umum, Anda dapat mengoptimalkan ukuran / bentuk potongan dengan menetapkan tupel bentuk (misalnya (2,2,2)dalam contoh sederhana di atas). Hal ini memungkinkan Anda untuk membuat pembacaan sepanjang sumbu tertentu lebih efisien atau mengoptimalkan pembacaan / penulisan dengan ukuran tertentu.


Perbandingan kinerja I / O

Hanya untuk menekankan intinya, mari bandingkan membaca dalam potongan dari kumpulan data HDF5 yang terpotong dan array 3D pesanan Fortran yang besar (~ 8 GB) yang berisi data persis sama.

Saya telah membersihkan semua cache OS di antara setiap proses, jadi kami melihat performa "dingin".

Untuk setiap jenis file, kami akan menguji pembacaan dalam potongan x "penuh" di sepanjang sumbu pertama dan garis miring z "penuh" di sepanjang sumbu terakhir. Untuk larik memmapped berurutan Fortran, potongan "x" adalah kasus terburuk, dan potongan "z" adalah kasus terbaik.

Kode yang digunakan ada dalam sebuah intisari (termasuk membuat hdffile). Saya tidak dapat dengan mudah membagikan data yang digunakan di sini, tetapi Anda dapat mensimulasikannya dengan array nol dengan bentuk ( 621, 4991, 2600)dan tipe np.uint8.

The chunked_hdf.pyterlihat seperti ini:

import sys
import h5py

def main():
    data = read()

    if sys.argv[1] == 'x':
        x_slice(data)
    elif sys.argv[1] == 'z':
        z_slice(data)

def read():
    f = h5py.File('/tmp/test.hdf5', 'r')
    return f['seismic_volume']

def z_slice(data):
    return data[:,:,0]

def x_slice(data):
    return data[0,:,:]

main()

memmapped_array.pyserupa, tetapi memiliki sentuhan yang lebih rumit untuk memastikan irisan benar-benar dimuat ke dalam memori (secara default, memmappedlarik lain akan dikembalikan, yang tidak akan menjadi perbandingan apel-ke-apel).

import numpy as np
import sys

def main():
    data = read()

    if sys.argv[1] == 'x':
        x_slice(data)
    elif sys.argv[1] == 'z':
        z_slice(data)

def read():
    big_binary_filename = '/data/nankai/data/Volumes/kumdep01_flipY.3dv.vol'
    shape = 621, 4991, 2600
    header_len = 3072

    data = np.memmap(filename=big_binary_filename, mode='r', offset=header_len,
                     order='F', shape=shape, dtype=np.uint8)
    return data

def z_slice(data):
    dat = np.empty(data.shape[:2], dtype=data.dtype)
    dat[:] = data[:,:,0]
    return dat

def x_slice(data):
    dat = np.empty(data.shape[1:], dtype=data.dtype)
    dat[:] = data[0,:,:]
    return dat

main()

Mari kita lihat kinerja HDF terlebih dahulu:

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python chunked_hdf.py z
python chunked_hdf.py z  0.64s user 0.28s system 3% cpu 23.800 total

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python chunked_hdf.py x
python chunked_hdf.py x  0.12s user 0.30s system 1% cpu 21.856 total

Irisan-x "penuh" dan irisan-z "penuh" membutuhkan waktu yang kurang lebih sama (~ 20 detik). Mengingat ini adalah array 8GB, itu tidak terlalu buruk. Sebagian besar waktu

Dan jika kita membandingkannya dengan waktu array yang dipetakan (urutannya sesuai Fortran: "z-slice" adalah kasus terbaik dan "x-slice" adalah kasus terburuk.):

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python memmapped_array.py z
python memmapped_array.py z  0.07s user 0.04s system 28% cpu 0.385 total

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python memmapped_array.py x
python memmapped_array.py x  2.46s user 37.24s system 0% cpu 3:35:26.85 total

Ya, Anda membacanya dengan benar. 0,3 detik untuk satu arah irisan dan ~ 3,5 jam untuk yang lainnya.

Waktu untuk mengiris ke arah "x" jauh lebih lama daripada jumlah waktu yang dibutuhkan untuk memuat seluruh larik 8GB ke dalam memori dan memilih potongan yang kita inginkan! (Sekali lagi, ini adalah larik berurutan Fortran. Waktu irisan x / z yang berlawanan akan terjadi pada larik berurutan C.)

Namun, jika kita selalu ingin mengambil bagian di sepanjang arah kasus terbaik, array biner besar pada disk sangat bagus. (~ 0,3 dtk!)

Dengan array yang dipetakan, Anda terjebak dengan perbedaan I / O ini (atau mungkin anisotropi adalah istilah yang lebih baik). Namun, dengan kumpulan data HDF yang dipotong, Anda dapat memilih ukuran potongan sedemikian rupa sehingga aksesnya sama atau dioptimalkan untuk kasus penggunaan tertentu. Ini memberi Anda lebih banyak fleksibilitas.

Singkatnya

Mudah-mudahan itu membantu menjernihkan satu bagian dari pertanyaan Anda, bagaimanapun juga. HDF5 memiliki banyak keunggulan dibandingkan memmap "mentah", tetapi saya tidak memiliki ruang untuk mengembangkan semuanya di sini. Kompresi dapat mempercepat beberapa hal (data yang saya gunakan tidak mendapatkan banyak manfaat dari kompresi, jadi saya jarang menggunakannya), dan cache tingkat OS sering kali berfungsi lebih baik dengan file HDF5 daripada dengan memmaps "mentah". Selain itu, HDF5 adalah format kontainer yang sangat fantastis. Ini memberi Anda banyak fleksibilitas dalam mengelola data Anda, dan dapat digunakan kurang lebih dari bahasa pemrograman apa pun.

Secara keseluruhan, cobalah dan lihat apakah itu berfungsi dengan baik untuk kasus penggunaan Anda. Saya pikir Anda mungkin akan terkejut.


3
Jawaban yang bagus. Saya ingin menambahkan bahwa Anda dapat menyesuaikan tata letak chunking Anda dengan pola akses data yang khas. Jika pola akses Anda memiliki ukuran stensil yang cukup dapat diprediksi, Anda biasanya dapat memilih pemotongan seperti untuk mencapai kecepatan yang mendekati optimal setiap saat.
Eelco Hoogendoorn

2
Jawaban yang bagus! Satu hal yang tidak disebutkan tentang chunking adalah efek dari cache chunk. Setiap set data terbuka memiliki chunk cache-nya sendiri, ukuran defaultnya adalah 1 MB, yang dapat disesuaikan menggunakan H5Pset_chunk_cache () di C. Biasanya berguna untuk mempertimbangkan berapa banyak potongan yang dapat disimpan dalam memori saat memikirkan pola akses Anda. Jika cache Anda dapat menampung, katakanlah, 8 potongan dan kumpulan data Anda adalah 10 potongan ke arah pemindaian, Anda akan banyak membuang dan kinerjanya akan buruk.
Dana Robinson
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.