2010-01-29 27 views
8

Tôi có các vùng bộ nhớ có thể được coi là "mảng bit". Họ là tương đương vớiBất kỳ cách nào thông minh hơn để trích xuất từ ​​mảng bit?

unsigned char arr[256]; 

Nhưng nó sẽ là suy nghĩ tốt hơn về như

bit arr[2048]; 

Tôi đang truy cập bit riêng biệt từ nó với

#define GETBIT(x,in) ((in)[ ((x)/8) ] & 1<<(7-((x)%8))) 

nhưng tôi làm điều đó rất nhiều trong nhiều vị trí của mã, thường trong các phần quan trọng về hiệu suất và tôi tự hỏi liệu có phương pháp thông minh hơn, tối ưu hơn nào để làm điều đó hay không.

thông tin bổ sung: Kiến trúc: ARM9 (32 bit); gcc/Linux. Không thể thay đổi biểu diễn dữ liệu vật lý - nó được cung cấp bên ngoài hoặc ánh xạ để sử dụng bên ngoài.

+3

Tôi biết có một số biến thể về số lượng bit trên mỗi char, nhưng 256 bit trên mỗi char là một _lot_. – MSalters

+0

Các phiên dịch: cảm ơn, cố định. –

Trả lời

6

Để truy cập ngẫu nhiên các bit riêng lẻ, macro bạn đã đề xuất là tốt như bạn sẽ nhận được (miễn là bạn bật tối ưu hóa trong trình biên dịch của mình).

Nếu có bất kỳ mẫu nào ở tất cả các bit bạn đang truy cập, thì bạn có thể làm tốt hơn. Ví dụ: nếu bạn thường truy cập cặp bit, thì bạn có thể thấy một số cải tiến bằng cách cung cấp một phương thức để nhận hai bit thay vì một bit, ngay cả khi bạn không phải lúc nào cũng sử dụng cả hai bit. Cũng như với bất kỳ vấn đề tối ưu hóa nào, bạn sẽ cần phải rất quen thuộc với hành vi mã của bạn, đặc biệt là các mẫu truy cập của nó trong mảng bit của bạn, để cải thiện hiệu suất có ý nghĩa.

Cập nhật: Vì bạn truy cập vào phạm vi bit, bạn có thể có thể siết chặt hiệu suất hơn trong số các macro của mình. Ví dụ, nếu bạn cần truy cập Bốn bit bạn có thể có các macro như thế này:

#define GETBITS_0_4(x,in) (((in)[(x)/8] & 0x0f)) 
#define GETBITS_1_4(x,in) (((in)[(x)/8] & 0x1e) >> 1) 
#define GETBITS_2_4(x,in) (((in)[(x)/8] & 0x3c) >> 2) 
#define GETBITS_3_4(x,in) (((in)[(x)/8] & 0x78) >> 3) 
#define GETBITS_4_4(x,in) (((in)[(x)/8] & 0xf0) >> 4) 
#define GETBITS_5_4(x,in) ((((in)[(x)/8] & 0xe0) >> 5) | (((in)[(x)/8+1] & 0x01)) << 3) 
#define GETBITS_6_4(x,in) ((((in)[(x)/8] & 0xc0) >> 6) | (((in)[(x)/8+1] & 0x03)) << 2) 
#define GETBITS_7_4(x,in) ((((in)[(x)/8] & 0x80) >> 7) | (((in)[(x)/8+1] & 0x07)) << 1) 
// ...etc 

Những macro sẽ cắt ra bốn bit từ mỗi vị trí bit 0, 1, 2, vv (Để giảm sự gia tăng ngoặc vô nghĩa, bạn có thể muốn sử dụng chức năng inline cho ở trên) Sau đó, có lẽ định nghĩa một hàm nội tuyến như:.

inline int GETBITS_4(int x, unsigned char *in) { 
    switch (x % 8) { 
     case 0: return GETBITS_0_4(x,in); 
     case 1: return GETBITS_1_4(x,in); 
     case 2: return GETBITS_2_4(x,in); 
     // ...etc 
    } 
} 

Do đây là rất nhiều mã boilerplate tẻ nhạt, đặc biệt là nếu bạn đã có nhiều độ rộng khác nhau , bạn có thể muốn viết chương trình để tạo tất cả các hàm truy cập GETBIT_*.

(Tôi nhận thấy rằng các bit trong byte của bạn được lưu trữ theo thứ tự ngược lại từ những gì tôi đã viết ở trên. Áp dụng một chuyển đổi thích hợp để phù hợp với cấu trúc của bạn nếu bạn cần.)

+0

Tôi thường truy cập -rang-bit, bắt đầu với ngẫu nhiên không liên kết * m * và kết thúc bằng một ngẫu nhiên * n * khác. –

+0

Tuyệt vời, đó là một cơ hội tuyệt vời. Tôi sẽ thêm nhiều hơn vào câu trả lời của tôi. –

+0

Tại thời điểm này, bạn muốn các mẫu thành thật. Xem câu trả lời của tôi. – MSalters

1

Nếu bạn đảo ngược thứ tự bit trong 'arr', thì bạn có thể loại bỏ phần tử khỏi macro. Đó là những gì tốt nhất mà tôi có thể nói, mà không có kiến ​​thức về bối cảnh vấn đề (làm thế nào các bit được sử dụng).

0

Tại sao không tạo lớp trình bao bọc của riêng bạn?

Sau đó, bạn có thể thêm bit vào "mảng" bằng cách sử dụng toán tử như + và lấy lại các bit riêng lẻ bằng toán tử [].

Macro của bạn có thể được cải thiện bằng cách sử dụng & 7 thay vì% 8 nhưng có khả năng trình biên dịch sẽ thực hiện tối ưu hóa đó cho bạn.

Gần đây tôi đã làm chính xác những gì bạn đang làm và luồng của tôi có thể bao gồm bất kỳ số bit nào.

Vì vậy, tôi có một cái gì đó như sau:

BitStream<1> oneBitBitStream; 
BitStream<2> twoBitBitStream; 

oneBitBitStream += Bit_One; 
oneBitBitStream += Bit_Zero; 

twoBitBitStream += Bit_Three; 
twoBitBitStream += Bit_One; 

và vân vân. Nó làm cho mã dễ đọc và bạn có thể cung cấp một giao diện giống như STL cho nó để hỗ trợ faimilarity :)

7

Tôi không nghĩ vậy. Trong thực tế, nhiều kiến ​​trúc CPU sẽ không truy cập từng bit một.

Trên C++ bạn có std::bitset<N>. nhưng có thể không có hiệu suất cao nhất tùy thuộc vào việc triển khai và tối ưu hóa trình biên dịch của bạn.

BTW, có thể tốt hơn khi nhóm mảng bit của bạn thành uint32_t[32] (hoặc uint64_t[16]) cho dereferencing được căn chỉnh (trong đó bitset sẽ thực hiện điều này cho bạn).

+3

Tại sao bạn nghĩ rằng 'std :: bitset ' đặc biệt chậm? Như bạn đã lưu ý một cách chính xác, trên nhiều CPU truy cập các bit riêng lẻ không chỉ là nhanh, nhưng tôi thấy không có lý do gì khiến std :: bitset <> cộng thêm chi phí. Trong thực tế, nó có thể nhanh hơn 'char []' vì nó có thể loại bỏ các vấn đề bí ẩn. – MSalters

+1

+1 để MSalters bình luận - std :: bitset - với tối ưu hóa được kích hoạt - không nên khác biệt so với macro, chỉ có ít cơ hội hơn cho các lỗi. Theo mặc định, mã kiểu STL có thể chậm hơn trong các bản dựng gỡ lỗi - nếu bạn nhận thấy các biến thể micro giây. – peterchen

+0

+1 đây là câu trả lời đúng, mặc dù tôi không biết tại sao poster tin rằng 'bitet' sẽ chậm hơn so với việc tung ra của riêng bạn; 'bitset' được tối ưu hóa cao trên hầu hết các hệ thống –

0

Kể từ khi câu hỏi được gắn thẻ với C++, có lý do gì bạn không thể đơn giản sử dụng tiêu chuẩn bitset?

+1

chỉ khi nó có thể phân bổ nó trên một khu vực bộ nhớ được xác định trước. Một thứ quan trọng là bitmap được tạo bởi thư viện cho một màn hình. Ngoài ra, nó có hiệu quả hơn không? (thiết bị nhúng, tài nguyên hạn chế, viết tắt về hiệu năng) –

+0

@SF: Bạn có thể sử dụng std :: bitset với bộ phân bổ tùy chỉnh sử dụng mảng bit được ánh xạ bộ nhớ của bạn. –

1
#define GETBIT(x,in) ((in)[ ((x)/8) ] & 1<<(7-((x)%8))) 

có thể được tối ưu hóa.

1) Sử dụng int chuẩn thường là kiểu dữ liệu nguyên có thể truy cập nhanh nhất. Nếu bạn không cần phải di chuyển, bạn có thể tìm ra kích thước của một int với kích thước và thích ứng với mã sau đây.

2)

#define GETBIT(x,in) ((in)[ ((x) >>> 3) ] & 1<<((x) & 7)) 

Nhà điều hành mod% là chậm hơn so với ANDing. Và bạn không cần phải trừ, chỉ cần điều chỉnh thói quen SETBIT của bạn.

+0

Tôi thích '((x) & 7)' thay vì '((x)% 8)' nhưng tôi hy vọng trình biên dịch sẽ sử dụng tối ưu hóa này. Dấu ngoặc vuông nằm ngay trong '#define GETBIT (x, in) ((in) [((x) >>> 3)] & 1 << ((x) & 7))' phải không '#define GETBIT (x, in) ((in) [((x) >>> 3) & 1 << ((x) & 7))]' – iain

+0

Không bao giờ chuyển tiếp các tối ưu hóa mà trình biên dịch có thể thực hiện hay không. Nếu bạn có thể tối ưu hóa nó, làm điều đó, để lại nó cho trình biên dịch những người có thể hoặc có thể không có khả năng tối ưu hóa nó. – George

3

Lấy giải pháp của Greg làm căn cứ:

template<unsigned int n, unsigned int m> 
inline unsigned long getbits(unsigned long[] bits) { 
    const unsigned bitsPerLong = sizeof(unsigned long) * CHAR_BIT 
    const unsigned int bitsToGet = m - n; 
    BOOST_STATIC_ASSERT(bitsToGet < bitsPerLong); 
    const unsigned mask = (1UL << bitsToGet) - 1; 
    const size_t index0 = n/bitsPerLong; 
    const size_t index1 = m/bitsPerLong; 
    // Do the bits to extract straddle a boundary? 
    if (index0 == index1) { 
    return (bits[index0] >> (n % bitsPerLong)) & mask; 
    } else { 
    return ((bits[index0] >> (n % bitsPerLong)) + (bits[index1] << (bitsPerLong - (m % bitsPerLong)))) & mask; 
    } 
} 

có thể nhận được ít nhất 32 bit, ngay cả khi họ không phù hợp. Lưu ý đó là cố ý inline vì bạn không muốn có nhiều chức năng này.

0

Thay vì mảng char chưa được ký và macro tùy chỉnh, bạn có thể sử dụng std::vector<bool>. Mẫu lớp vectơ có một chuyên môn mẫu đặc biệt cho kiểu bool. Chuyên môn này được cung cấp để tối ưu hóa cho phân bổ không gian: Trong chuyên môn mẫu này, mỗi phần tử chỉ chiếm một bit (ít hơn 8 lần so với loại nhỏ nhất trong C++: char).

+1

'vector ' dường như "không được chấp nhận", mặc dù, bất cứ nơi nào bạn nhìn (chính xác vì nó chỉ giả vờ là một vectơ), và dường như bị thay thế bởi 'dynamic_bitset' (nếu kích thước được xác định tại thời gian biên dịch, 'std :: bitset' có thể phù hợp hơn). – visitor

Các vấn đề liên quan