2011-09-21 24 views
6

Tôi cần phải có số 8 bit trên cpu 64 bit và chuyển nó sang phải 8 lần. Mỗi lần tôi thay đổi số tôi cần phải thay đổi cùng một số 8 bit ở phía sau nó để tôi kết thúc với cùng một số 8 bit lặp lại 8 lần. Điều này sẽ kết thúc được thay đổi, thêm 8, thay đổi thêm 8 ... vv mà kết thúc lên được 40 + chu kỳ (chính xác cho tôi nếu tôi sai).Có hướng dẫn cpu thay đổi và sao chép có thể được truy cập từ C#?

Có cách nào để thực hiện thao tác này (dịch chuyển và sao chép) trong 1 chu kỳ sao cho cuối cùng tôi có cùng giá trị không?

long _value = 0; 
byte _number = 7; 
for (int i = 0; i < 8; i++) { 
    _value = (_value << 8) + _number; 
} 

EDIT: Tôi đang cố gắng so sánh luồng của ký tự để phát hiện từ khóa. Tôi không thể sử dụng string.contains bởi vì giá trị chuỗi có thể được trên ranh giới của bộ đệm. Ngoài ra, ứng dụng phải chạy trên CPU ARM được tích hợp cũng như CPU ​​máy tính để bàn và máy chủ. Sử dụng bộ nhớ và chu kỳ CPU là rất quan trọng.

+0

Tôi không nghĩ rằng loại chức năng này tồn tại. C# là một ngôn ngữ cấp cao. Nếu bạn cần các loại tối ưu hóa này, hãy sử dụng ngôn ngữ cấp thấp như C. – meziantou

+0

@Chris Unsafe cho phép bạn sử dụng con trỏ không sử dụng hướng dẫn mới. – meziantou

+0

"kết thúc bằng 40 chu kỳ" - chu kỳ x86/x64 là ảo như MSIL. Rất khó để dự đoán làm thế nào điều này sẽ làm việc ra trong hướng dẫn RISC pipelined. –

Trả lời

4

Một ý tưởng khác sẽ là tính toán trước tất cả cho tất cả các giá trị của byte một bảng tra cứu.

var lu = new long[256]; 
// init 
var n = 7; 
var v = lu[n]; 

Cập nhật

Một số kết quả benchmark (tính bằng ms mỗi 100000000 lặp):

  • Loop: 272
  • unrolled: 207
  • không an toàn: 351
  • Lookup : 250
  • HenkH: 216

Phiên bản unrolled là:

long _value = 0; 
byte _number = 7; 

_value = (_value + _number) << 8; 
_value = (_value + _number) << 8; 
_value = (_value + _number) << 8; 
_value = (_value + _number) << 8; 
_value = (_value + _number) << 8; 
_value = (_value + _number) << 8; 
_value = (_value + _number) << 8; 
_value = (_value + _number) << 8; 

Phiên bản không an toàn là:

long _value = 0; 
byte _number = 7; 

byte* p = (byte*)&_value; 

*p++ = _number; 
*p++ = _number; 
*p++ = _number; 
*p++ = _number; 
*p++ = _number; 
*p++ = _number; 
*p++ = _number; 
*p++ = _number; 

Đáng buồn là không thực hiện :(

Các tra cứu chỉ là một đọc vào một mảng.

Tất cả được biên dịch cho x64/bản phát hành.

+1

Ít nhất 1 điều được xác nhận: Bạn không thể biết chỉ cần nhìn vào mã): –

+1

@HenkHolterman: Và cái không an toàn cũng đẹp quá! – leppie

+0

Leppie, có một vài điều chỉnh bạn có thể thử: 'long' nên có được' ulong' tất cả cùng tôi tự hỏi nếu '|' thay vì '+' sẽ là đáng chú ý. –

3

Khi bạn muốn nó được nhanh chóng, bạn ít nhất có thể cuộn vòng lặp của bạn:

ulong _value = 0; 
byte _number = 7; 

_value = _number; 
_value = (_value << 8) + _value; 
_value = (_value << 16) + _value; 
_value = (_value << 32) + _value; 

này sẽ có các chi nhánh ít quá.

+0

Đây không phải là nhanh hơn: bạn nên viết chỉ là một biểu thức. MSIL sẽ tạo công thức là RPN, tránh một số hoạt động (vô dụng). –

+0

Tôi đã ấn tượng rằng trình biên dịch/jit đã làm rất nhiều thứ như thế này cho bạn. Nó có ý nghĩa trong trường hợp này, nhưng tôi ngần ngại tự ý unroll vòng cho 'hiệu suất' lý do. Giá trị của nó là một thử, nhưng tôi chắc chắn sẽ chuẩn nó để xem hiệu suất thực tế đạt được. – captncraig

+0

Điểm chính là sử dụng trung gian để giảm số bước. Tôi hy vọng/hy vọng rằng người Jitter sẽ tối ưu hóa những phát biểu này như một nhóm, nếu không thì @Mario là đúng. Tôi sẽ không mong đợi Jitter để giảm nó từ 8 đến 4 bước. –

6

Hiện tại, không có kết nối trực tiếp giữa số lượng hướng dẫn được thực hiện và số lượng chu kỳ CPU cần thiết để thực thi chúng. Bạn cũng có vẻ giả định rằng một tuyên bố trong C# tương ứng với một lệnh lắp ráp/cpu đơn cũng sai.

Mã của bạn có vẻ đúng với những gì mô tả thuật toán của bạn nói (lưu ý rằng từ lâu đã được ký, sử dụng ulong cho hành vi chưa được ký).

Nếu bạn muốn sử dụng các phần mở rộng CPU chuyên biệt (như mmx, sse, bất cứ điều gì) có thể thực hiện phép chuyển bổ trợ trong một lệnh, bạn cần sử dụng mã lắp ráp. Nhưng tôi không chắc chắn nếu như một hướng dẫn cụ thể tồn tại. Điều này có thể phụ thuộc vào loại CPU bạn có.

Bạn không thể sử dụng mã lắp ráp trực tiếp cùng với C#, nhưng bạn có thể sử dụng lắp ráp cùng với c (hoặc là tệp liên kết đối tượng sử dụng làm cho nó lắp ráp nội tuyến). Mã c được biên dịch có thể được sử dụng từ C# /. Net với interop.

Nhưng câu hỏi đầu tiên và quan trọng đối với bạn nên là: Bạn đang cố gắng làm gì?

Tôi nghi ngờ rằng hiệu suất này rất quan trọng đối với ứng dụng của bạn và thậm chí nếu bạn nên tự hỏi chính mình nếu C# là ngôn ngữ tốt nhất cho mục tiêu của bạn.

+0

@codymainx: Có một vài nơi trong thuật toán đề xuất mà tôi cố gắng tối ưu hóa. Vì điều này được thiết kế để làm việc trong ứng dụng chạy dài, bất kỳ sự tăng hiệu suất nào (không có nhiều chi phí phát triển) sẽ là một lợi thế. –

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