摘要:
对普通链表进行添加和删除操作,会创建和销毁对象,如果操作太频繁会对GC造成压力,而游标链表是事先分配好一个大数组,然后用数组的下标代替普通链表的引用指针,这样链表节点的添加删除,只是下标指向的改变,不会创建和销毁对象,相当于自己管理了内存,所以降低了GC的压力。
性能测试模型:
1、往链表里添加500w个int节点
2、把这500w个节点删除
3、重复1和2进行10次,分别记录各代gc回收次数,gc堆大小及执行时间。
性能测试结果
游标链表
err:0
gen:24,gen:15,gen:5
totalMemory:160217492
tack time:19358
.NET自带普通链表
gen:213,gen:125,gen:21
totalMemory:120221380
tack time:18024
性能测试结论:
对游标链表的频繁添加、删除操作引起的GC回收次数明显比普通链表要少的多,但游标链表因为使用了额外的数组记录空闲节点列表,所以占用内存大一些,由于每个标准操作的子步骤数比普通链表多一些,所以总体销号时间也稍大于普通链表,但不明显。
单元测试用例:
操作:
1、往链表的头上依次添加节点1,2,3加上默认的0节点共4个节点。
2、从链表的尾部连续删除两个节点
3、从链表的尾部开始,向前遍历节点,并打印节点数据
预期
输出数据2,3
单元测试结果:
通过
测试部分代码
游标链表实现代码
为了简单起见,只是先了在头部添加和移除尾部节点的方法。
参考链接:
CursorList.cpp - Implementation for cursor linked list
http://www.cplusplus.happycodings.com/Data-Structures-and-Algorithm-Analysis-in-C++/code33.html
链表的游标法实现 cursor_list
http://blog.csdn.net/liuzongqiang/archive/2008/01/06/2027762.aspx
对普通链表进行添加和删除操作,会创建和销毁对象,如果操作太频繁会对GC造成压力,而游标链表是事先分配好一个大数组,然后用数组的下标代替普通链表的引用指针,这样链表节点的添加删除,只是下标指向的改变,不会创建和销毁对象,相当于自己管理了内存,所以降低了GC的压力。
性能测试模型:
1、往链表里添加500w个int节点
2、把这500w个节点删除
3、重复1和2进行10次,分别记录各代gc回收次数,gc堆大小及执行时间。
性能测试结果
游标链表
err:0
gen:24,gen:15,gen:5
totalMemory:160217492
tack time:19358
.NET自带普通链表
gen:213,gen:125,gen:21
totalMemory:120221380
tack time:18024
性能测试结论:
对游标链表的频繁添加、删除操作引起的GC回收次数明显比普通链表要少的多,但游标链表因为使用了额外的数组记录空闲节点列表,所以占用内存大一些,由于每个标准操作的子步骤数比普通链表多一些,所以总体销号时间也稍大于普通链表,但不明显。
单元测试用例:
操作:
1、往链表的头上依次添加节点1,2,3加上默认的0节点共4个节点。
2、从链表的尾部连续删除两个节点
3、从链表的尾部开始,向前遍历节点,并打印节点数据
预期
输出数据2,3
单元测试结果:
通过
测试部分代码
class
Program {
static void Main( string [] args) {
// UnitTest();
const int max = 500 * 10000 ;
Stopwatch watch = Stopwatch.StartNew();
// TestLinkdList(max, 10); // 这一句和下一句要分别测试,别同时测
TestCursorList(max, 10 );
Console.WriteLine( " gen:{0},gen:{1},gen:{2} " ,
GC.CollectionCount( 0 ),GC.CollectionCount( 1 ),GC.CollectionCount( 2 ));
Console.WriteLine( " totalMemory:{0} " ,GC.GetTotalMemory( false ));
Console.WriteLine( " tack time:{0} " ,watch.ElapsedMilliseconds);
Console.ReadKey();
}
private static void UnitTest() {
CursorList < int > list = new CursorList < int > ( 3 );
list.AddHeader( 1 );
list.AddHeader( 2 );
list.AddHeader( 3 );
list.RemoveTail();
list.RemoveTail();
CursorListNode < int > node = list.Tail;
while (node != null ) // 预期输出2,3
{
Console.WriteLine(node.Data);
node = node.Next;
}
}
private static void TestCursorList( int max, int iteration)
{
CursorList < int > list = new CursorList < int > (max);
int err = 0 ;
for ( int k = 0 ; k < iteration; k ++ ) {
for ( int i = 0 ; i < max; i ++ )
if (list.AddHeader(i) == null ) err ++ ;
for ( int i = 0 ; i < max; i ++ )
list.RemoveTail();
}
Console.WriteLine( " err:{0} " , err);
}
private static void TestLinkdList( int max, int iteration) {
LinkedList < int > list = new LinkedList < int > ();
for ( int k = 0 ; k < iteration; k ++ ) {
for ( int i = 0 ; i < max; i ++ )
list.AddFirst(i);
for ( int i = 0 ; i < max; i ++ )
list.RemoveLast();
}
}
}
static void Main( string [] args) {
// UnitTest();
const int max = 500 * 10000 ;
Stopwatch watch = Stopwatch.StartNew();
// TestLinkdList(max, 10); // 这一句和下一句要分别测试,别同时测
TestCursorList(max, 10 );
Console.WriteLine( " gen:{0},gen:{1},gen:{2} " ,
GC.CollectionCount( 0 ),GC.CollectionCount( 1 ),GC.CollectionCount( 2 ));
Console.WriteLine( " totalMemory:{0} " ,GC.GetTotalMemory( false ));
Console.WriteLine( " tack time:{0} " ,watch.ElapsedMilliseconds);
Console.ReadKey();
}
private static void UnitTest() {
CursorList < int > list = new CursorList < int > ( 3 );
list.AddHeader( 1 );
list.AddHeader( 2 );
list.AddHeader( 3 );
list.RemoveTail();
list.RemoveTail();
CursorListNode < int > node = list.Tail;
while (node != null ) // 预期输出2,3
{
Console.WriteLine(node.Data);
node = node.Next;
}
}
private static void TestCursorList( int max, int iteration)
{
CursorList < int > list = new CursorList < int > (max);
int err = 0 ;
for ( int k = 0 ; k < iteration; k ++ ) {
for ( int i = 0 ; i < max; i ++ )
if (list.AddHeader(i) == null ) err ++ ;
for ( int i = 0 ; i < max; i ++ )
list.RemoveTail();
}
Console.WriteLine( " err:{0} " , err);
}
private static void TestLinkdList( int max, int iteration) {
LinkedList < int > list = new LinkedList < int > ();
for ( int k = 0 ; k < iteration; k ++ ) {
for ( int i = 0 ; i < max; i ++ )
list.AddFirst(i);
for ( int i = 0 ; i < max; i ++ )
list.RemoveLast();
}
}
}
游标链表实现代码
为了简单起见,只是先了在头部添加和移除尾部节点的方法。
public
class
CursorListNode
<
T
>
{
public CursorListNode < T > Next { get ; set ; }
public CursorListNode < T > Prior { get ; set ; }
internal int Index { get ; set ; }
public T Data { get ; set ; }
}
public class CursorList < T > {
private readonly Queue < int > _freeQ = null ;
private readonly CursorListNode < T > [] _list = null ;
public CursorList( int capacity) {
_freeQ = new Queue < int > (capacity + 1 );
_list = new CursorListNode < T > [capacity + 1 ];
_list[ 0 ] = Header = Tail = new CursorListNode < T > {
Index = 0 ,
Data = default (T),
Next = null ,
Prior = null
};
for ( int i = 1 ; i < capacity + 1 ; i ++ )
{
_freeQ.Enqueue(i);
_list[i] = new CursorListNode < T > {
Index = - 1 ,
Data = default (T),
Next = null ,
Prior = null
};
}
}
public CursorListNode < T > AddHeader(T data) {
if (_freeQ.Count < 1 ) return null ;
int newIndex = _freeQ.Dequeue();
CursorListNode < T > newNode = _list[newIndex];
newNode.Index = newIndex;
newNode.Data = data;
newNode.Next = null ;
newNode.Prior = Header;
Header.Next = newNode;
Header = newNode;
return newNode;
}
public void RemoveTail() {
if (Tail == null ) return ;
_freeQ.Enqueue(Tail.Index);
if (Tail.Next != null )
{
Tail = _list[Tail.Next.Index];
Tail.Prior = null ;
}
else
Tail = null ;
}
public CursorListNode < T > Header { get ; set ; }
public CursorListNode < T > Tail { get ; set ; }
}
public CursorListNode < T > Next { get ; set ; }
public CursorListNode < T > Prior { get ; set ; }
internal int Index { get ; set ; }
public T Data { get ; set ; }
}
public class CursorList < T > {
private readonly Queue < int > _freeQ = null ;
private readonly CursorListNode < T > [] _list = null ;
public CursorList( int capacity) {
_freeQ = new Queue < int > (capacity + 1 );
_list = new CursorListNode < T > [capacity + 1 ];
_list[ 0 ] = Header = Tail = new CursorListNode < T > {
Index = 0 ,
Data = default (T),
Next = null ,
Prior = null
};
for ( int i = 1 ; i < capacity + 1 ; i ++ )
{
_freeQ.Enqueue(i);
_list[i] = new CursorListNode < T > {
Index = - 1 ,
Data = default (T),
Next = null ,
Prior = null
};
}
}
public CursorListNode < T > AddHeader(T data) {
if (_freeQ.Count < 1 ) return null ;
int newIndex = _freeQ.Dequeue();
CursorListNode < T > newNode = _list[newIndex];
newNode.Index = newIndex;
newNode.Data = data;
newNode.Next = null ;
newNode.Prior = Header;
Header.Next = newNode;
Header = newNode;
return newNode;
}
public void RemoveTail() {
if (Tail == null ) return ;
_freeQ.Enqueue(Tail.Index);
if (Tail.Next != null )
{
Tail = _list[Tail.Next.Index];
Tail.Prior = null ;
}
else
Tail = null ;
}
public CursorListNode < T > Header { get ; set ; }
public CursorListNode < T > Tail { get ; set ; }
}
参考链接:
CursorList.cpp - Implementation for cursor linked list
http://www.cplusplus.happycodings.com/Data-Structures-and-Algorithm-Analysis-in-C++/code33.html
链表的游标法实现 cursor_list
http://blog.csdn.net/liuzongqiang/archive/2008/01/06/2027762.aspx