Việc lập hồ sơ ứng dụng C# của tôi cho biết rằng thời gian đáng kể được chi tiêu trong List<T>.AddRange
. Sử dụng Reflector để nhìn vào đoạn code trong phương pháp này chỉ ra rằng nó gọi List<T>.InsertRange
đó được thực hiện như vậy:Danh sách <T> .Thêm vào thực hiện tối ưu hóa
public void InsertRange(int index, IEnumerable<T> collection)
{
if (collection == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
}
if (index > this._size)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
}
ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
int count = is2.Count;
if (count > 0)
{
this.EnsureCapacity(this._size + count);
if (index < this._size)
{
Array.Copy(this._items, index, this._items, index + count, this._size - index);
}
if (this == is2)
{
Array.Copy(this._items, 0, this._items, index, index);
Array.Copy(this._items, (int) (index + count), this._items, (int) (index * 2), (int) (this._size - index));
}
else
{
T[] array = new T[count]; // (*)
is2.CopyTo(array, 0); // (*)
array.CopyTo(this._items, index); // (*)
}
this._size += count;
}
}
else
{
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
this.Insert(index++, enumerator.Current);
}
}
}
this._version++;
}
private T[] _items;
Người ta có thể tranh luận rằng sự đơn giản của giao diện (chỉ có một tình trạng quá tải của InsertRange) biện minh cho các chi phí thực hiện thời gian chạy kiểu cheching và đúc. Nhưng điều gì có thể là lý do đằng sau 3 dòng tôi đã chỉ ra với (*)
? Tôi nghĩ rằng nó có thể được viết lại để thay thế nhanh hơn:
is2.CopyTo(this._items, index);
Bạn có thấy bất kỳ lý do để không sử dụng này đơn giản và dường như thay thế nhanh hơn?
Edit:
Cảm ơn câu trả lời. Vì vậy, ý kiến đồng thuận là đây là biện pháp bảo vệ chống lại việc thu thập dữ liệu đầu vào thực hiện CopyTo theo cách bị lỗi/độc hại. Đối với tôi, có vẻ như quá mức cần phải trả giá 1) kiểm tra kiểu thời gian chạy 2) phân bổ động của mảng tạm 3) tăng gấp đôi thao tác sao chép, khi tất cả điều này có thể đã được lưu bằng cách xác định 2 hoặc một vài quá tải của InsertRange , một người nhận được IEnumerable
như bây giờ, người thứ hai nhận được List<T>
, thứ ba nhận được T[]
. Hai phiên bản sau có thể đã được triển khai để chạy nhanh gấp hai lần trong trường hợp hiện tại.
Chỉnh sửa 2:
tôi đã thực hiện một danh sách nhanh lớp, giống với Liệt kê, ngoại trừ việc nó cũng cung cấp một tình trạng quá tải của AddRange mà phải mất một T [] lập luận. Quá tải này không cần xác minh loại động và sao chép hai phần tử. Tôi đã làm hồ sơ này FastList.AddRange chống List.AddRange bằng cách thêm mảng 4-byte 1000 lần vào một danh sách mà ban đầu là emtpy. Thực hiện của tôi đánh bại tốc độ của List.AddRange tiêu chuẩn với một hệ số 9 (chín!). List.AddRange mất khoảng 5% thời gian chạy trong một trong các kịch bản sử dụng quan trọng của ứng dụng của chúng tôi, thay thế Danh sách với một lớp cung cấp AddRange nhanh hơn có thể cải thiện thời gian chạy ứng dụng lên 4%.
@shojtsy: đảm bảo bạn nắm bắt chỉnh sửa 2 :) –
Bạn đã sử dụng trường hợp xấu nhất cho thử nghiệm của mình. Hãy thử chèn một 'int' duy nhất với' Chèn' thay vì mảng 4 byte với 'ChènRange' và bạn sẽ nhận được nhiều dấu vết hơn. –
Kịch bản thử nghiệm là đại diện cho việc sử dụng thực tế trong ứng dụng của tôi. Danh sách này là một luồng byte trong đó các mảng 4-10 byte được nối nhiều lần. Tôi hiểu rằng việc chuyển một Mảng đơn giản tới AddRange là nơi mà các hình phạt về hiệu suất của việc triển khai chuẩn được hiển thị rõ nhất. Đó chính là ý của tôi. – shojtsy