考虑以下方法:
static Stopwatch ArrayTest(int size)
{
var arr = new int[size];
Stopwatch stw = new Stopwatch();
stw.Start();
for (int i = 0; i < size; i++)
{
arr[i] = i / div;
}
var rnd = new Random(1);
var sz2 = size / div;
for (int i = 0; i < sz2; i++)
{
var sz = size - i;
var ix = rnd.Next(sz);
Array.Copy(arr, ix + 1, arr, ix, size - ix - 1);
arr[sz - 1] = 0;
}
double sum = 0.0;
for (int i = 0; i < size - sz2; i++)
{
sum += arr[i];
}
stw.Stop();
Console.Write(" Array: {0}", sum);
return stw;
}
static Stopwatch ListTest(int size)
{
var lst = new List<int>();
Stopwatch stw = new Stopwatch();
stw.Start();
for (int i = 0; i < size; i++)
{
lst.Add(i / div);
}
var rnd = new Random(1);
var sz2 = size / div;
for (int i = 0; i < sz2; i++)
{
var ix = rnd.Next(lst.Count);
lst.RemoveAt(ix);
}
double sum = 0.0;
for (int i = 0; i < lst.Count; i++)
{
sum += lst[i];
}
stw.Stop();
Console.Write(" List: {0}", sum);
return stw;
}
div = 2且size = 200000。 运行此(在发布时编译)会产生以下结果:
Array: 5012641699 12.8367529 s
List: 5012641699 6.1027289 s
根据http://referencesource.microsoft.com/#mscorlib List.RemoveAt实现如下:
// Removes the element at the given index. The size of the list is
// decreased by one.
//
public void RemoveAt(int index) {
if ((uint)index >= (uint)_size) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
Contract.EndContractBlock();
_size--;
if (index < _size) {
Array.Copy(_items, index + 1, _items, index, _size - index);
}
_items[_size] = default(T);
_version++;
}
所以我想知道为什么ArrayTest需要的时间是ListTest的两倍。在我看来,他们都做了几乎相同的事情,实际上我希望ArrayTest由于更少的开销而更快。也许我错过了一些明显的东西?
更新 让我解释一下这个例子。我们的想法是在随机删除条件下测量性能(同时仍然保留索引访问)。每个方法的第一部分初始化一个数组/列表,其中数组中的每个元素是索引的1/2(整数除法)。下一部分随机删除1/2元素。最后一个循环简单地将数组/列表中剩余的值相加,用作校验和(用于比较结果)。对代码进行概要分析表明,在ArrayTest中,Array.Copy使用的时间最多,而在ListTest中,lst.RemoveAt使用的时间最多。
我将尝试在更简单的示例中复制行为并在此处进行更新。
答案 0 :(得分:0)
您的测试计划存在缺陷。你已经让它过于复杂,因而很难在纸上证明。找出代码所做的唯一方法是通过调试,手工计算所有内容。
所以我做了一个更简单的测试。
void Main()
{
const int LENGTH = 200000;
Stopwatch sw = Stopwatch.StartNew();
var a = new int[LENGTH];
for (int index = LENGTH-1; index > 0; index--)
Array.Copy(a, 1, a, 0, index);
sw.Stop();
sw.ElapsedMilliseconds.Dump();
var l = new List<int>(a);
sw = Stopwatch.StartNew();
for (int index = LENGTH-1; index > 0; index--)
l.RemoveAt(0);
sw.Stop();
sw.ElapsedMilliseconds.Dump();
}
它只是“删除”数组的第0个元素和列表。我机器上的结果是:
3366
3442
3270
3242
3343
3385
那是3次跑。
答案 1 :(得分:0)
问题似乎在于:
Array.Copy(arr, ix + 1, arr, ix, size - ix - 1);
方法ArrayTest
中的。它应该读
Array.Copy(arr, ix + 1, arr, ix, sz - ix - 1);
因为在调整数组大小时不改变变量size
,而是在每次迭代中计算实际大小sz
。