2012-01-20 29 views
10

Tôi đã tối ưu hóa một phương pháp mở rộng để so sánh hai luồng cho bình đẳng (byte-cho-byte) - biết rằng đây là một phương pháp nóng tôi đã cố gắng tối ưu hóa nó càng nhiều càng tốt có thể tiếp cận với độ dài nhiều megabyte). Tôi về cơ bản đã đưa ra phương pháp sau:Nếu tuyên bố đúng khối thực thi khi điều kiện là sai

[StructLayout(LayoutKind.Explicit)] 
struct Converter 
{ 
    [FieldOffset(0)] 
    public Byte[] Byte; 

    [FieldOffset(0)] 
    public UInt64[] UInt64; 
} 

/// <summary> 
/// Compares two streams for byte-by-byte equality. 
/// </summary> 
/// <param name="target">The target stream.</param> 
/// <param name="compareTo">The stream to compare the target to.</param> 
/// <returns>A value indicating whether the two streams are identical.</returns> 
public static bool CompareBytes(this Stream target, Stream compareTo) 
{ 
    if (target == null && compareTo == null) 
     return true; 
    if (target == null || compareTo == null) 
     return false; 
    if (target.Length != compareTo.Length) 
     return false; 
    if (object.ReferenceEquals(target, compareTo)) 
     return true; 
    if (!target.CanRead || !target.CanSeek) 
     throw new ArgumentOutOfRangeException("target"); 
    if (!compareTo.CanRead || !compareTo.CanSeek) 
     throw new ArgumentOutOfRangeException("target"); 
    lock (target) 
    { 
     lock (compareTo) 
     { 
      var origa = target.Position; 
      var origb = compareTo.Position; 
      try 
      { 
       target.Position = compareTo.Position = 0; 

       // Shrink the number of comparisons. 
       var arr1 = new byte[4096]; 
       var convert1 = new Converter() { Byte = arr1 }; 
       var arr2 = new byte[4096]; 
       var convert2 = new Converter() { Byte = arr2 }; 

       int len; 
       while ((len = target.Read(arr1, 0, 4096)) != 0) 
       { 
        if (compareTo.Read(arr2, 0, 4096) != len) 
         return false; 
        for (var i = 0; i < (len/8) + 1; i++) 
         if (convert1.UInt64[i] != convert2.UInt64[i]) 
          return false; 
       } 

       return true; 
      } 
      finally 
      { 
       target.Position = origa; 
       compareTo.Position = origb; 
      } 
     } 
    } 
} 

Vấn đề là các convert1.UInt64[i] != convert2.UInt64[i]if khối (trở về false) đang được đánh giá ngay cả khi giá trị là như nhau. Tôi đã kiểm tra từng cá nhân, sau đó kiểm tra kết quả của 'không bằng'. tôi trong sự hoài nghi tinh khiết:

Values are not equal

Tôi đã không sai lầm với con trỏ hướng dẫn - đây là cách mã thực thi và pin đồng hồ đang hoạt động.

Bất kỳ ý tưởng nào về điều này có thể xảy ra?

+0

có hoạt động không nếu bạn thêm {} – rerun

+1

giống như so sánh tham chiếu (đối tượng khác nhau, luôn sai) đang diễn ra thay vì so sánh giá trị – Alex

+0

Tôi nhầm lẫn, cả thuộc tính struct đều có FieldOffset 0, bạn biết bạn đang so sánh táo và táo? – mtijn

Trả lời

11
for (var i = 0; i < (len/8) + 1; i++) 

Trình gỡ lỗi nói chung gặp khó khăn với công đoàn này, nó không thể hiển thị nội dung mảng khi tôi thử. Nhưng vấn đề cốt lõi là không có nghi ngờ +1 trong biểu thức kết thúc cho(). Điều đó chỉ mục mảng vượt quá phần tử cuối cùng của nó khi len chia hết cho 8. Thời gian chạy không thể bắt lỗi này, chồng chéo các mảng khiến thuộc tính Độ dài có giá trị không có thật. Điều gì xảy ra tiếp theo là hành vi không xác định, bạn đang đọc các byte không phải là một phần của mảng. Cách giải quyết là làm cho mảng 7 byte dài hơn.

Loại mã này không chính xác là tối ưu hóa, đọc và so sánh uint64 trên máy 32 bit là tốn kém, đặc biệt khi mảng không được căn chỉnh chính xác .. Khoảng 50% tỷ lệ cược cho điều đó. Một cái bẫy chuột tốt hơn là sử dụng C runtime memcmp() chức năng, có sẵn trên bất kỳ máy tính Windows:

[DllImport("msvcrt.dll")] 
    private static extern int memcmp(byte[] arr1, byte[] arr2, int cnt); 

Và sử dụng nó như thế này:

int len; 
    while ((len = target.Read(arr1, 0, 4096)) != 0) { 
     if (compareTo.Read(arr2, 0, 4096) != len) return false; 
     if (memcmp(arr1, arr2, len) != 0) return false; 
    } 
    return true; 

Đừng so sánh về hiệu suất của điều này với một đồng bằng cho() vòng lặp so sánh byte. Các ga cuối cùng ở đây là băng thông bộ nhớ bus.

+0

Cảm ơn Hans. Tôi đã nghĩ đến việc đặt +1 trong một cuộc gọi 'Max'.Bạn đã trả lời câu hỏi của tôi và đi xa hơn và hơn thế nữa và cung cấp cho tôi một tối ưu hóa tốt hơn nữa. Một đánh dấu rất xứng đáng! –

1

Các vấn đề như thế này thường là vấn đề với sự hiểu biết về cách tối ưu hóa hoạt động. Dòng mã này có thể được thực hiện rất tốt bởi vì cả hai mệnh đề false trả về được kết hợp thành một tập hợp các lệnh ở cấp thấp hơn. Các nguyên nhân khác cho vấn đề như thế này là nếu kiến ​​trúc bạn đang cho phép thực thi có điều kiện trong đó một số lệnh nhất định được nhấn trong trình gỡ lỗi nhưng kết quả không bao giờ được cam kết với các thanh ghi ở cấp kiến ​​trúc.

Xác minh rằng mã hoạt động ở chế độ gỡ lỗi trước tiên. Sau đó, khi bạn được thuyết phục kết quả là giống như chế độ phát hành, hãy xem các hướng dẫn cơ bản để tìm ra tối ưu hóa trình biên dịch trong tầm tay.

+0

Mã này hiện đang gỡ lỗi - thậm chí vì vậy tôi sẽ giả định JITter sẽ không tối ưu hóa điều này theo cách mà có thể phá vỡ nó. Tôi sẽ kiểm tra sự bất hòa và xem những gì đang xảy ra dưới vỏ bọc. –

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