2012-03-23 32 views
13

Trong khi phản ánh với ILSpy tôi tìm thấy dòng mã này trong Queue<T>.Enqueue(T item) -method:Strange Queue <T> .Enqueue (T item) mã

if (this._size == this._array.Length) 
{ 
    int num = (int)((long)this._array.Length * 200L/100L); 
    if (num < this._array.Length + 4) 
    { 
     num = this._array.Length + 4; 
    } 
    this.SetCapacity(num); 
} 

Tôi chỉ tự hỏi tại sao ai đó sẽ làm điều này? Tôi nghĩ rằng đó là một số loại kiểm tra tràn số nguyên, nhưng tại sao lại nhân trước với 200L và sau đó chia cho 100L?

Có thể đây là vấn đề với các trình biên dịch trước đó?

+0

num được sử dụng để làm gì? – UrbanEsc

+0

@UrbanEsc Nó được sử dụng để thay đổi kích thước mảng –

+0

Nó không thể liên quan đến tràn, vì '(dài) _array.Length * 200' sẽ không bao giờ tràn. Công cụ '+ 4' là đảm bảo rằng mảng vẫn phát triển ngay cả khi kích thước ban đầu của nó bằng 0. –

Trả lời

4

Thông thường những thứ đầu tiên nhân với số chia cho 100 là tính toán tỷ lệ phần trăm - Có lẽ có một số const XxxPercentage = 200 hoặc một cái gì đó tương tự như vậy trong mã ban đầu. Trình biên dịch dường như không tối ưu hóa số * 200/100 thành * 2.

Mã này đặt dung lượng gấp đôi kích thước của nó - nhưng nếu gấp đôi kích thước của nó sẽ nhỏ hơn kích thước ban đầu + 4, hãy sử dụng mã đó thay thế.

Lý do nó được chuyển đổi thành dài có lẽ là vì nếu bạn nhân một số nguyên với "200 phần trăm", nó sẽ tràn.

4

Dưới đây là tất cả như nhau và sẽ tạo ra kết quả tương tự:

int size = (int)((length * 200L)/100L); 
int size = length << 1; 
int size = length * 2; 

Lý do cho việc lựa chọn tùy chọn đầu tiên trong khác là để cho thấy ý định của bạn rõ ràng:

const long TotalArraySize = 200L; 
const long BytesPerElement = 100L; 
return (length * TotalArraySize)/BytesPerElement; 

Một số chi tiết về các tác động về hiệu suất được đưa ra ở đây: Doubling a number - shift left vs. multiplication

+3

Bạn có chắc chắn rằng việc quảng cáo 'length' thành' long', sau đó thực hiện nhân với số lượng dài theo sau là khoảng cách dài thực sự đủ điều kiện làm tối ưu hóa hiệu suất không? –

+0

@TeomanSoygul Câu trả lời của bạn không sai, nhưng đó vẫn là câu hỏi tại sao trình biên dịch không tối ưu hóa '200L/100L'. –

0

Mục đích của * 200L/100L không rõ ràng hơn * 2 theo ý kiến ​​của tôi. Lý do duy nhất tôi có thể nghĩ tại sao nó được thực hiện như thế là để đảm bảo chiều dài hàng đợi có thể tăng lên đến 200 lần. Có sự khác biệt trong * 200/100* 2 sao cho kết quả đầu tiên sẽ dẫn đến ngoại lệ tràn cho số nhỏ hơn 100 lần. Ví dụ, nếu đó là cho các giá trị byte, x * 200/100 sẽ thất bại cho x == 2 nhưng * 2 sẽ thất bại chỉ khi x là lớn như 128.

Nhưng khi Marcelo Cantos chỉ ra (long)this._array.Length * 200L/100L sẽ không bao giờ tràn nên câu trả lời của tôi là có lẽ không giúp đỡ nhiều.

Nó có thể là 'tính năng' của ILSpy không? Có lẽ trong mã nguồn nó chỉ là * 2.

EDIT

Sau khi điều tra bổ sung có vẻ rằng mã lạ này phải là một artefact của một số refactoring. Tôi đã kiểm tra cách nó được thực hiện trong Danh sách <>

private void EnsureCapacity(int min) 
{ 
    if (this._items.Length < min) 
    { 
     int num = (this._items.Length == 0) ? 4 : (this._items.Length * 2); 
     if (num < min) 
     { 
      num = min; 
     } 
     this.Capacity = num; 
    } 
} 

Điều này cũng đơn giản như bạn mong đợi. Tôi đoán là mã Queue.Enqueue đã được làm lại nhưng không được dọn dẹp hoàn toàn và một số mã lạ xuất phát từ thay đổi này.Hầu hết các nhà phát triển giả định rằng các thư viện của Microsoft là hoàn hảo và mọi thứ đều có ý nghĩa nhưng có khả năng là không phải mọi dòng mã đều được viết bởi một thiên tài :-)

4

Nếu bạn tiếp tục tìm cách thực hiện Queue, bạn sẽ tìm thấy các trường sau:

const int _GrowFactor = 200; 
const int _MinimumGrow = 4; 

Điểm thú vị là các hằng số này không được sử dụng :) Tôi nghĩ rằng các hằng số này được mã hóa thay thế (yếu tố tăng trưởng cũng được thay thế bằng loại dài). Cho phép tìm phương pháp Enqueue từ quan điểm này:

if (this._size == this._array.Length) 
{ 
    int capacity = (int)((this._array.Length * _GrowFactor)/100L); 
    if (capacity < (this._array.Length + _MinimumGrow)) 
    { 
     capacity = this._array.Length + _MinimumGrow; 
    } 
    this.SetCapacity(capacity); 
} 

Tôi nghĩ những tên đó có ý nghĩa. GrowFactor quy định cụ thể về phần trăm mảng nên phát triển. Đây là 200% theo mặc định. Nhưng họ cũng chỉ định phát triển tối thiểu cho mảng nội bộ. Vì vậy, nếu mảng không phát triển quá nhiều như chiều dài hiện tại + tối thiểu phát triển, chúng tôi cung cấp cho sự phát triển tối thiểu này anyway.

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