views:

500

answers:

8

I am trying to implement a singly linked list in C. A common implementation that you see floating around the internet is something like

typedef struct {
  int head;
  Node *tail;
} Node;

with methods like

Node cons(int head, Node tail) {
  Node y;
  y.head = head;
  y.tail = malloc(sizeof(Node));
  *y.tail = tail;
}

Performance is very important. Is there any way to implement a linked list in C that will be faster than this? For example, getting rid of the memory allocation (y.tail = malloc(sizeof(Node))) should provide a significant speed increase.

+9  A: 

Very fast appending to a linked list? A rope (not limited to strings with small modifications) would allow you to batch allocate memory (improving perfomance) while not penalizing appending to the end of the list.

Yann Ramin
Linked lists don't perform well. +1 for suggesting the only sensible answer here -- use another data structure! (Preferably something like std::deque or std::vector!)
Billy ONeal
@Billy, I don't understand why your comment got voted up since the question is specifically about **C** and you are advocating a C++ solution.
JeremyP
@Jeremy you can implement something like a vector in C
Pete Kirkham
Rope is essentially a binary tree. For some operations, it is slower than a list. It may also involve more memory allocation, depending on the access pattern.
@JeremyP: What @Pete said. The words "something like" do actually mean something :)
Billy ONeal
But implement "something like std::vector in C" is not saying anything other than "use a different better data structure". What is the implementation of std::vector?
JeremyP
+13  A: 

Yes there is... This is called as a memory pool. Similar to a thread pool. Basically you allocate an area of memory at the beginning of the program of type Node. Pointers to this area are stored in an array. In your cons function all you do is get the pointers from the array. This does not improve the overall speed, but if you have frequent memory allocations this will increase the responsiveness of the program at the cost of some space for the array

Ram Bhat
+1. You *really* need to be pre-allocating the space for Node in pools. Individual allocations will kill you if you're creating lots of them.
Nick Bastin
Yea... but for a linked list like the one he's shown, it doesnt really matter :P .
Ram Bhat
+1. Yes, this is should be an effective solution.
+2  A: 

If you are concerned with malloc fragmentation, you could request a large multiple number of size Node and keep incrementing a pointer by sizeof(Node) every time you copy Node values.

Jeff Meatball Yang
This is probably better than the array solutions others suggest. You have to keep track of your pool and know when to allocate more, and know when all the pointers have been returned.
Jon L
+5  A: 

What operation shall be fast: insertion, retrieval, all ? There is always a trade-off. Does your solution need to be scalable ? Linked list are not.

Should you want/need to stick to a linked list, you could store it into an array of structs having a field indicating the index of the next entry in the linked list. Insertion will be very fast, without any allocation, the downside being you have to know the number of elements in advance - or to reallocate the table when it's getting full.

Refer to the "Linked lists using arrays of nodes" subsection of the Wikipedia page on linked list.

philippe
A: 

I would suggest you to use the linux kernel list implementation:

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=include/linux/list.h

(no additional memory allocation)

Nicolas Viennot
A lot of performance issues are related to pointer locality and memory allocation. As this implementation does not implement these parts, I do not think it is useful to Michael Dickens.
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=include/linux/list.h#l368It even prefetch the next pointer for the next iteration. Trust me, those list in the kernel are VERY optimized
Nicolas Viennot
+1  A: 

Memory concerns aside, some thoughts on making single link lists faster.

I think there is a bit of confusion in your code. At it's very core a linked list is:

typdef struct _node {
     ...
     struct _node *next;
} NODE;

Most implementations would have a void * in there to hang the payload on. That is not particularly important for now.

List inserts must connect the pointers. For simply adding the node (and ignoring the adding the payload) there 1 assignment if the node is going at the head or tail of a list, and 2 if it is going in the middle. There isn't much that can be done to get around that.

Occasionally simple lists only use node structures, so tail inserting requires traversal. This is costly. Having a special head structure with knowledge of the first and last node removes that cost.

Traversals can be made faster by implementing this as a skip-list (http://en.wikipedia.org/wiki/Skip_list). Though some care needs to be taken during node insertion to optimize (and you get more pointer assignments during insertion).

Jon L
A: 

Check the speed of this singly linked list implementation as well:

http://web.archive.org/web/20071103112712/http://stsdas.stsci.edu/bps/linked_list.html

curoo
A: 

If the only operations you need is pushing, popping and iterating then instead of linked list you could put elements into an array. Pushing and popping element is just to modify one number (index of the first or the last element). Front and back of the array can be cyclically connected so elements can be freely pushed without a problem that the array ends.

adf88