Có lợi thế về tốc độ phân tích hoặc sử dụng bộ nhớ khi sử dụng HDF5 để lưu trữ mảng lớn (thay vì các tệp nhị phân phẳng) không?


96

Tôi đang xử lý các mảng 3D lớn, mà tôi thường cần cắt theo nhiều cách khác nhau để thực hiện nhiều phân tích dữ liệu. Một "khối lập phương" điển hình có thể là ~ 100GB (và có thể sẽ lớn hơn trong tương lai)

Có vẻ như định dạng tệp được đề xuất điển hình cho bộ dữ liệu lớn trong python là sử dụng HDF5 (h5py hoặc pytables). Câu hỏi của tôi là: có bất kỳ lợi ích nào về tốc độ hoặc việc sử dụng bộ nhớ khi sử dụng HDF5 để lưu trữ và phân tích các khối này thay vì lưu trữ chúng trong các tệp nhị phân phẳng đơn giản không? HDF5 có thích hợp hơn cho dữ liệu dạng bảng, trái ngược với các mảng lớn như những gì tôi đang làm việc không? Tôi thấy rằng HDF5 có thể cung cấp khả năng nén tốt, nhưng tôi quan tâm hơn đến tốc độ xử lý và xử lý tình trạng tràn bộ nhớ.

Tôi thường chỉ muốn phân tích một tập hợp con lớn của khối lập phương. Một nhược điểm của cả pytables và h5py là có vẻ như khi tôi lấy một phần của mảng, tôi luôn nhận được một mảng trống trở lại, sử dụng hết bộ nhớ. Tuy nhiên, nếu tôi cắt một bản đồ nhỏ của một tệp nhị phân phẳng, tôi có thể có được một chế độ xem, giữ dữ liệu trên đĩa. Vì vậy, có vẻ như tôi có thể dễ dàng phân tích các vùng cụ thể của dữ liệu mà không cần sử dụng quá nhiều bộ nhớ.

Tôi đã khám phá cả pytables và h5py, và cho đến nay vẫn chưa thấy lợi ích của cả hai đối với mục đích của tôi.


1
HDF là một định dạng tệp "chunked". Trung bình, nó sẽ cho bạn đọc nhanh hơn nhiều đối với một phần tùy ý trong tập dữ liệu của bạn. Một bản ghi nhớ sẽ có trường hợp tốt nhất nhanh, nhưng trường hợp xấu nhất rất, rất chậm. h5pyphù hợp hơn với các tập dữ liệu như của bạn hơn pytables. Ngoài ra, h5pykhông không trả về một mảng NumPy trong bộ nhớ. Thay vào đó, nó trả về một cái gì đó hoạt động giống như một cái, nhưng không được tải vào bộ nhớ (tương tự như một memmappedmảng). Tôi đang viết một câu trả lời đầy đủ hơn (có thể không kết thúc nó), nhưng hy vọng nhận xét này sẽ giúp một chút trong thời gian chờ đợi.
Joe Kington

Cảm ơn. Tôi đồng ý rằng h5py trả về một tập dữ liệu tương tự như một bản đồ. Tuy nhiên, nếu bạn thực hiện một phần của tập dữ liệu h5py, nó sẽ trả về một mảng không rõ ràng, mà tôi tin rằng (?) Có nghĩa là dữ liệu đã được đưa vào bộ nhớ một cách không cần thiết. Một memmamp trả về một dạng xem cho bản đồ gốc nếu có thể. Nói cách khác: type(cube)cho h5py._hl.dataset.Dataset. Trong khi type(cube[0:1,:,:])cho numpy.ndarray.
Caleb

Tuy nhiên, quan điểm của bạn về thời gian đọc trung bình rất thú vị.
Caleb

4
Nếu bạn có nút thắt cổ chai I / O thì trong nhiều trường hợp, việc nén thực sự có thể cải thiện hiệu suất đọc / ghi (đặc biệt là sử dụng các thư viện nén nhanh như BLOSC và LZO), vì nó làm giảm băng thông I / O cần thiết với một số chu kỳ CPU bổ sung. . Bạn có thể muốn xem trang này , trang này có nhiều thông tin về cách tối ưu hóa hiệu suất đọc-ghi bằng cách sử dụng tệp PyTables HDF5.
ali_m

2
"nếu tôi cắt một bản ghi nhớ nhỏ của một tệp nhị phân phẳng, tôi có thể có được một chế độ xem, giữ dữ liệu trên đĩa" - điều đó có thể đúng, nhưng nếu bạn thực sự muốn làm bất cứ điều gì với các giá trị trong mảng đó thì sớm hay muộn bạn sẽ phải tải chúng vào RAM. Một mảng được ánh xạ bộ nhớ chỉ cung cấp một số đóng gói để bạn không phải suy nghĩ về chính xác khi nào dữ liệu được đọc hoặc liệu nó có vượt quá dung lượng bộ nhớ hệ thống của bạn hay không. Trong một số trường hợp, hành vi lưu vào bộ nhớ đệm riêng của các mảng được ghi nhớ có thể thực sự rất kém tối ưu .
ali_m

Câu trả lời:


159

Ưu điểm của HDF5: Tổ chức, linh hoạt, khả năng tương tác

Một số ưu điểm chính của HDF5 là cấu trúc phân cấp (tương tự như thư mục / tệp), siêu dữ liệu tùy ý tùy chọn được lưu trữ với từng mục và tính linh hoạt của nó (ví dụ: nén). Cấu trúc tổ chức và lưu trữ siêu dữ liệu này nghe có vẻ tầm thường, nhưng nó rất hữu ích trong thực tế.

Một ưu điểm khác của HDF là tập dữ liệu có thể có kích thước cố định hoặc kích thước linh hoạt. Do đó, thật dễ dàng để nối dữ liệu vào một tập dữ liệu lớn mà không cần phải tạo toàn bộ bản sao mới.

Ngoài ra, HDF5 là một định dạng chuẩn hóa với các thư viện có sẵn cho hầu hết mọi ngôn ngữ, vì vậy việc chia sẻ dữ liệu trên đĩa của bạn giữa Matlab, Fortran, R, C và Python rất dễ dàng với HDF. (Công bằng mà nói, nó cũng không quá khó với một mảng nhị phân lớn, miễn là bạn biết thứ tự C so với F và biết hình dạng, loại, v.v. của mảng được lưu trữ.)

Ưu điểm của HDF cho một mảng lớn: I / O nhanh hơn của một lát tùy ý

Cũng giống như TL / DR: Đối với mảng 3D dung lượng ~ 8GB, việc đọc một lát "đầy đủ" dọc theo bất kỳ trục nào mất ~ 20 giây với tập dữ liệu HDF5 phân khúc và 0,3 giây (trường hợp tốt nhất) đến hơn ba giờ (trường hợp xấu nhất) cho một mảng được ghi nhớ của cùng một dữ liệu.

Ngoài những thứ được liệt kê ở trên, có một lợi thế lớn khác đối với định dạng dữ liệu trên đĩa "chunked" * chẳng hạn như HDF5: Đọc một lát tùy ý (nhấn mạnh vào tùy ý) thường sẽ nhanh hơn nhiều, vì dữ liệu trên đĩa liền kề hơn Trung bình cộng.

*(HDF5 không nhất thiết phải là một định dạng dữ liệu phân đoạn. Nó hỗ trợ phân đoạn, nhưng không yêu cầu. Trên thực tế, mặc định để tạo tập dữ liệu h5pykhông phải là phân đoạn, nếu tôi nhớ chính xác.)

Về cơ bản, tốc độ đọc đĩa trong trường hợp tốt nhất của bạn và tốc độ đọc đĩa trong trường hợp xấu nhất của bạn đối với một phần nhất định của tập dữ liệu của bạn sẽ khá gần với tập dữ liệu HDF phân khúc (giả sử bạn đã chọn kích thước chunk hợp lý hoặc để thư viện chọn cho bạn). Với một mảng nhị phân đơn giản, trường hợp tốt nhất sẽ nhanh hơn, nhưng trường hợp xấu nhất thì tệ hơn nhiều .

Một lưu ý, nếu bạn có SSD, bạn có thể sẽ không nhận thấy sự khác biệt lớn về tốc độ đọc / ghi. Tuy nhiên, với một ổ cứng thông thường, việc đọc tuần tự nhanh hơn rất nhiều so với việc đọc ngẫu nhiên. (ví dụ: Ổ cứng thông thường có thời seekgian sử dụng lâu .) HDF vẫn có lợi thế hơn trên SSD, nhưng đó là do các tính năng khác của nó (ví dụ siêu dữ liệu, tổ chức, v.v.) hơn là do tốc độ thô.


Trước hết, để giải quyết sự nhầm lẫn, việc truy cập một h5pytập dữ liệu trả về một đối tượng hoạt động khá giống với một mảng numpy, nhưng không tải dữ liệu vào bộ nhớ cho đến khi nó bị cắt. (Tương tự như memmap, nhưng không giống nhau.) Hãy xem phần h5pygiới thiệu để biết thêm thông tin.

Cắt tập dữ liệu sẽ tải một tập hợp con dữ liệu vào bộ nhớ, nhưng có lẽ bạn muốn làm gì đó với nó, lúc đó bạn vẫn cần nó trong bộ nhớ.

Nếu bạn muốn thực hiện các tính toán ngoài lõi, bạn có thể khá dễ dàng đối với dữ liệu dạng bảng với pandashoặc pytables. Điều đó có thể xảy ra với h5py(đẹp hơn đối với các mảng ND lớn), nhưng bạn cần phải thả xuống mức thấp hơn và tự xử lý việc lặp lại.

Tuy nhiên, tương lai của các tính toán ngoài lõi giống như numpy là Blaze. Hãy xem nó nếu bạn thực sự muốn đi theo con đường đó.


Trường hợp "unchunked"

Trước hết, hãy xem xét một mảng 3D có thứ tự C được ghi vào đĩa (tôi sẽ mô phỏng nó bằng cách gọi arr.ravel()và in kết quả, để làm cho mọi thứ rõ ràng hơn):

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]]])

Các giá trị sẽ được lưu trữ tuần tự trên đĩa như thể hiện ở dòng 4 bên dưới. (Hãy bỏ qua chi tiết và phân mảnh hệ thống tệp vào lúc này.)

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])

Trong trường hợp tốt nhất, hãy lấy một lát dọc theo trục đầu tiên. Lưu ý rằng đây chỉ là 36 giá trị đầu tiên của mảng. Đây sẽ là một bài đọc rất nhanh! (một lần tìm kiếm, một lần đọc)

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]])

Tương tự, lát cắt tiếp theo dọc theo trục đầu tiên sẽ chỉ là 36 giá trị tiếp theo. Để đọc một lát hoàn chỉnh dọc theo trục này, chúng ta chỉ cần một seekthao tác. Nếu tất cả những gì chúng ta sẽ đọc là các lát cắt khác nhau dọc theo trục này, thì đây là cấu trúc tệp hoàn hảo.

Tuy nhiên, hãy xem xét trường hợp xấu nhất: Một lát dọc theo trục cuối cùng.

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]])

Để đọc phần này, chúng ta cần 36 lần tìm và 36 lần đọc, vì tất cả các giá trị đều được phân tách trên đĩa. Không ai trong số họ liền kề!

Điều này có vẻ khá nhỏ, nhưng khi chúng ta tiến tới các mảng ngày càng lớn, số lượng và kích thước của các seekhoạt động sẽ tăng lên nhanh chóng. Đối với một mảng 3D lớn (~ 10Gb) được lưu trữ theo cách này và đọc qua memmap, việc đọc toàn bộ phần dọc theo trục "tồi tệ nhất" có thể dễ dàng mất hàng chục phút, ngay cả với phần cứng hiện đại. Đồng thời, một lát dọc theo trục tốt nhất có thể mất ít hơn một giây. Để đơn giản, tôi chỉ hiển thị các lát "đầy đủ" dọc theo một trục duy nhất, nhưng điều tương tự cũng xảy ra với các lát tùy ý của bất kỳ tập con dữ liệu nào.

Ngẫu nhiên, có một số định dạng tệp tận dụng lợi thế này và về cơ bản lưu trữ ba bản sao của mảng 3D khổng lồ trên đĩa: một theo thứ tự C, một theo thứ tự F và một ở trung gian giữa hai. (Một ví dụ về điều này là định dạng D3D của Geoprobe, mặc dù tôi không chắc nó được ghi lại ở bất kỳ đâu.) Ai quan tâm nếu kích thước tệp cuối cùng là 4TB, dung lượng lưu trữ rất rẻ! Điều điên rồ về điều đó là bởi vì trường hợp sử dụng chính là trích xuất một lát phụ duy nhất theo mỗi hướng, các lần đọc bạn muốn thực hiện rất rất nhanh. Nó hoạt động rất tốt!


Trường hợp "chunked" đơn giản

Giả sử chúng tôi lưu trữ các "khối" 2x2x2 của mảng 3D dưới dạng các khối liền kề trên đĩa. Nói cách khác, một cái gì đó như:

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])

Vì vậy, dữ liệu trên đĩa sẽ giống như chunkedsau:

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])

Và chỉ để cho thấy rằng chúng là các khối 2x2x2 arr, hãy lưu ý rằng đây là 8 giá trị đầu tiên của chunked:

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

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

Để đọc trong bất kỳ phần nào dọc theo một trục, chúng tôi sẽ đọc trong 6 hoặc 9 phần liền kề (gấp đôi dữ liệu mà chúng tôi cần) và sau đó chỉ giữ lại phần mà chúng tôi muốn. Đó là trường hợp xấu nhất tối đa là 9 lần tìm kiếm so với tối đa 36 lần tìm kiếm cho phiên bản không phân khúc. (Nhưng trường hợp tốt nhất vẫn là 6 lần tìm kiếm so với 1 lần tìm kiếm đối với mảng được ánh xạ.) Bởi vì đọc tuần tự rất nhanh so với lần tìm kiếm, điều này làm giảm đáng kể lượng thời gian cần thiết để đọc một tập hợp con tùy ý vào bộ nhớ. Một lần nữa, hiệu ứng này trở nên lớn hơn với các mảng lớn hơn.

HDF5 tiến xa hơn một vài bước. Các phần không cần phải được lưu trữ liền kề và chúng được lập chỉ mục bởi B-Tree. Hơn nữa, chúng không nhất thiết phải có cùng kích thước trên đĩa, do đó, nén có thể được áp dụng cho từng đoạn.


Mảng chunked với h5py

Theo mặc định, h5pykhông tạo các tệp HDF phân khối trên đĩa ( pytablesngược lại, tôi nghĩ là có). chunks=TrueTuy nhiên, nếu bạn chỉ định khi tạo tập dữ liệu, bạn sẽ nhận được một mảng phân đoạn trên đĩa.

Như một ví dụ nhanh, tối thiểu:

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?'

Lưu ý rằng chunks=Trueyêu h5pycầu tự động chọn kích thước phân đoạn cho chúng tôi. Nếu bạn biết thêm về trường hợp sử dụng phổ biến nhất của mình, bạn có thể tối ưu hóa kích thước / hình dạng phân đoạn bằng cách chỉ định một bộ hình dạng (ví dụ: (2,2,2)trong ví dụ đơn giản ở trên). Điều này cho phép bạn thực hiện đọc dọc theo một trục cụ thể hiệu quả hơn hoặc tối ưu hóa cho việc đọc / ghi ở một kích thước nhất định.


So sánh hiệu suất I / O

Chỉ để nhấn mạnh vấn đề, chúng ta hãy so sánh việc đọc theo từng lát từ một tập dữ liệu HDF5 được chia nhỏ và một mảng 3D lớn (~ 8GB), theo thứ tự của Fortran chứa cùng một dữ liệu chính xác.

Tôi đã xóa tất cả bộ nhớ cache của hệ điều hành giữa mỗi lần chạy, vì vậy chúng tôi đang thấy hiệu suất "lạnh".

Đối với mỗi loại tệp, chúng tôi sẽ kiểm tra việc đọc trong một lát cắt x "đầy đủ" dọc theo trục đầu tiên và một lát cắt z "đầy đủ" dọc theo trục cuối cùng. Đối với mảng được ánh xạ theo thứ tự Fortran, lát cắt "x" là trường hợp xấu nhất và lát "z" là trường hợp tốt nhất.

Mã được sử dụng là một ý chính (bao gồm cả việc tạo hdftệp). Tôi không thể dễ dàng chia sẻ dữ liệu được sử dụng ở đây, nhưng bạn có thể mô phỏng nó bằng một mảng các số không có cùng hình dạng ( 621, 4991, 2600)và kiểu np.uint8.

Các chunked_hdf.pyngoại hình như thế này:

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.pytương tự, nhưng phức tạp hơn để đảm bảo các lát cắt thực sự được tải vào bộ nhớ (theo mặc định, một memmappedmảng khác sẽ được trả về, không phải là so sánh giữa táo với táo).

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()

Trước tiên, hãy xem hiệu suất HDF:

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

Một lát x "đầy đủ" và một lát z "đầy đủ" mất khoảng thời gian như nhau (~ 20 giây). Xem xét đây là một mảng 8GB, điều đó không quá tệ. Hầu hết thời gian

Và nếu chúng ta so sánh điều này với thời gian của mảng được ghi nhớ (đó là theo thứ tự Fortran: "z-slice" là trường hợp tốt nhất và "x-slice" là trường hợp xấu nhất.):

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

Bạn đã đọc đúng. 0,3 giây cho một hướng lát và ~ 3,5 giờ cho hướng kia.

Thời gian để lát trong "x" chỉ đạo là xa dài hơn khoảng thời gian nó sẽ mất để tải toàn bộ mảng 8GB vào bộ nhớ và chọn slice chúng tôi muốn! (Một lần nữa, đây là một mảng được sắp xếp theo thứ tự Fortran. Thời gian lát cắt x / z ngược lại sẽ là trường hợp cho một mảng có thứ tự C.)

Tuy nhiên, nếu chúng ta luôn muốn đi theo hướng trường hợp tốt nhất, thì mảng nhị phân lớn trên đĩa là rất tốt. (~ 0,3 giây!)

Với một mảng được ghi nhớ, bạn sẽ mắc kẹt với sự khác biệt I / O này (hoặc có lẽ dị hướng là một thuật ngữ tốt hơn). Tuy nhiên, với tập dữ liệu HDF được phân loại, bạn có thể chọn kích thước khối sao cho quyền truy cập bằng nhau hoặc được tối ưu hóa cho một trường hợp sử dụng cụ thể. Nó mang lại cho bạn sự linh hoạt hơn rất nhiều.

Tóm tắt

Hy vọng rằng điều đó sẽ giúp làm sáng tỏ một phần câu hỏi của bạn, ở bất kỳ mức độ nào. HDF5 có nhiều lợi thế khác so với các bản ghi nhớ "thô", nhưng tôi không có chỗ để mở rộng tất cả chúng ở đây. Nén có thể tăng tốc một số thứ (dữ liệu tôi làm việc với không được lợi nhiều từ việc nén, vì vậy tôi hiếm khi sử dụng nó) và bộ nhớ đệm cấp hệ điều hành thường chơi với các tệp HDF5 hơn là với các bản ghi nhớ "thô". Ngoài ra, HDF5 là một định dạng container thực sự tuyệt vời. Nó mang lại cho bạn sự linh hoạt trong việc quản lý dữ liệu của mình và có thể được sử dụng ít nhiều từ bất kỳ ngôn ngữ lập trình nào.

Nhìn chung, hãy thử nó và xem nó có hoạt động tốt cho trường hợp sử dụng của bạn hay không. Tôi nghĩ bạn có thể ngạc nhiên.


3
Câu trả lời chính xác. Tôi muốn nói thêm rằng bạn có thể tùy chỉnh bố cục phân khúc theo kiểu truy cập dữ liệu điển hình của mình. Nếu bạn truy cập vào mẫu có kích thước khuôn mẫu khá dễ đoán, bạn thường có thể chọn phân khúc của mình chẳng hạn như để đạt được tốc độ gần tối ưu mọi lúc.
Eelco Hoogendoorn

2
Câu trả lời chính xác! Một điều không được đề cập về chunking là ảnh hưởng của bộ nhớ cache chunk. Mỗi tập dữ liệu mở có bộ nhớ đệm chunk riêng, kích thước mặc định là 1 MB, có thể được điều chỉnh bằng cách sử dụng H5Pset_chunk_cache () trong C. Nói chung, việc xem xét có thể giữ bao nhiêu phần trong bộ nhớ khi nghĩ về các kiểu truy cập của bạn. Nếu bộ nhớ cache của bạn có thể chứa 8 phần và tập dữ liệu của bạn là 10 phần theo hướng quét, bạn sẽ xáo trộn rất nhiều và hiệu suất sẽ rất khủng khiếp.
Dana Robinson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.