How is unrolled linked list better than linked list? - c

I was studying the book "Data Structure and algorithms made easy" and but I got confused while learning "Comparing Linked Lists and Unrolled Linked list"...
what is overhead?
Why he is only stating 8 bytes of overhead for 100 elements array?

Overhead is all the stuff that is not part of the data that you want to store. Like the pointers to the next and previous element.
The block list is a list of arrays. Each array contains a number of elements. In principle your entire list could consist of a single block node with an array of all your elements. So less overhead.
It's a bit confusing that head in LinkedBlock points to a ListNode - it should point to whatever the data is (without the prev and next pointers).

I think the book contains a serious error in the definition of struct LinkedBlock. Let's get back to that later and start with:
what is overhead?
The struct ListNode is designed for storing one integer but besides the integer each node has two pointers. So for each node you'll need to allocate 1 integer + 2 pointers. Let's assume 4 byte integer and 4 byte pointer. So each node will require 4 + 2x4 = 12 bytes. So in order to store 1 item of your real data (aka 1 integer) you need to allocate 12 bytes. You have wasted 8 bytes on pointers. These 8 "wasted" bytes are called overhead. They are used for bookkeeping only - not for data.
But it get worse than that... When allocating dynamic memory (which you normally do when using linked list) there are some additional overhead. The allocator may need a little extra memory for every malloc to store information about the malloc. Another issue is that malloc ed memory may aligned to some fixed block size (e.g. 16 or 32 byte) so if you allocate 20 byte there is no way to use the remaining 12 bytes - they are wasted. This is what the book calls "allocation overhead". The "allocation overhead" is system dependent but the book assumes 8 extra overhead bytes from each malloc.
So now each malloc 'ed struct ListNode takes up:
4 bytes for the integer
8 bytes for 2 pointers
8 bytes for allocation overhead
A total of 20 bytes where 4 bytes is for your data and 16 bytes are overhead. So for each integer you need to store, you'll need 20 bytes. And if you want to store 1000 integers, you end up wasting 16kb on overhead in order to store 4kb of data.
Now back to the struct LinkedBlock. In the book it looks like this:
struct LinkedBlock {
struct LinkedBlock *next;
struct LinkedNode *head;
int nodeCount;
};
I'm pretty sure there is a mistake in the book and that it should look like this instead:
struct LinkedBlock {
struct LinkedBlock *next;
int *dataArray;
int nodeCount;
};
The way to use this is something like:
struct LinkedBlock pNode = malloc(sizeof(struct LinkedBlock));
pNode->dataArray = malloc( 100 * sizeof(int) );
The first malloc requires 4 + 4 + 4 + 8 = 20 bytes. (pointer, pointer, int, allocation overhead)
The second malloc requires 4 * 100 + 8 = 408 bytes. (100 int, allocation overhead)
So a total of 428 bytes.
However, since the malloc'ed data can hold 100 integers (corresponding to 400 bytes), your overhead is only 28 bytes. In other words - in average you use 4.28 bytes for each integer. Compare that to the first method that required 20 bytes for each integer.
Why he is only stating 8 bytes of overhead for 100 elements array?
That was because the array was allocated in a single call and each malloc call is assumed to have 8 bytes allocation overhead.

In normal linked list, 1 node have 1 element and 2 pointer (8 bytes), 2 pointer is overhead since it's not your data. In unrolled linked list, 1 node have 100 element and 2 pointer (8 bytes), hence 8 bytes overhead for 100 element.

Related

extra padding between structs in C

I have struct in C:
typedef struct Node {
int data; // 4 bytes int + 4 bytes for alignment
struct Node* prev; // 8 bytes pointer
struct Node* next; // 8 bytes pointer
} Node;
The size of this struct is 24 bytes (8 + 8 + 8). When I use the sizeof(Node), the compiler also shows 24 bytes.
However, when I create two or more structs on the heap (one after another) and look at their memory location, there are 8 byte gaps between each Node struct.
For example:
11121344 (the 1st Node address)
11121376 (the 2nd Node address) // 376-344 = 32-24 = 8 extra bytes
11121408 (the 3rd Node address) // 408-376 = 32-24 = 8 extra bytes
Can you explain why compiler separates Node structs by adding 8 bytes between Nodes?
There are 2 possible reasons for your observation:
The C standard requires that malloc always returns memory chunks with maximum alignment to prevent alignment issues no matter what you allocate for.
malloc manages memory chunks internally by using some sort of data structures. Depending on the implementation, it would add additional information to each memory chunk for internal usage. For instance, malloc could manage memory chunks in a linked list, then it would require each chunk to hold an additional pointer that points to the next chunk.
The maximum alignment depends on the architecture and the compiler / malloc - implementation used.
For your case and assuming glibc, taken straight out of the docs of glibc/malloc.c :
Alignment: 2 * sizeof(size_t) (default)
(i.e., 8 byte alignment with 4byte size_t). This suffices for
nearly all current machines and C compilers. However, you can
define MALLOC_ALIGNMENT to be wider than this if necessary.
Minimum overhead per allocated chunk: 4 or 8 bytes
Each malloced chunk has a hidden word of overhead holding size
and status information.
Minimum allocated size: 4-byte ptrs: 16 bytes (including 4 overhead)
8-byte ptrs: 24/32 bytes (including, 4/8 overhead)
Thus malloc in your case will align to 2 * sizeof(size_t) = 16 bytes.
Also note the 'hidden overhead' mentioned. This overhead is due store additional internal information used for memory management...
Can you explain why compiler separates Node structs by adding 8 bytes between Nodes?
It's a coincidence. There is no rule about how to lay out memory for any sequence of malloc() calls.
The address can be ascending with a fixed interval, descending with varying intervals, (seemingly) random, ..., ....
If you want fixed relative addresses use an array
struct Node arr[3];
ptrdiff_t delta10 = &arr[1] - &arr[0];
ptrdiff_t delta20 = &arr[2] - &arr[0];
ptrdiff_t delta21 = &arr[2] - &arr[1];
if (delta10 != delta21) /* cannot happen */;
or allocate a group of elements (maybe with realloc()) at the same time
struct Node *elements = malloc(3 * sizeof *elements);
ptrdiff_t delta10 = &elements[1] - &elements[0];
ptrdiff_t delta20 = &elements[2] - &elements[0];
ptrdiff_t delta21 = &elements[2] - &elements[1];
if (delta10 != delta21) /* cannot happen */;
free(elements);

How much memory allocated for node in C?

typedef struct Node {
int data;
struct Node *next;
} node;
pointer->next = (node*)malloc(sizeof(node));
How many bytes of memory are dynamically given to pointer->next in the above code. For (int*)malloc(sizeof(int)), 2 bytes are given. Likewise how many for node?
Malloc will dinamically assign the size of "node".
Node is a struct and the size of every struct depends on the size of every element inside the struct.
In this case, the size of node will be: size of int + size of struct Node*
(If the result is not multiple of 2, it will be padded for architecture reasons)
Your device has an architecture of 2 bytes, and for that reason, the size of the structs can only be 2, 4, 6, 8 etc...
The size of int depends on the target you are working on. Since your architecture is 16 bits, the size of int is 2 bytes.
About. the size of struct Node *, you need to know that EVERY pointer data types have exactly the same size, it doesn't matter the data type their are pointing to. And that size also depends on the architecture. Again, your architecture is 16 bits and that's why the size of struct node * is 2 bytes.
size of int = 2.
size of struct node * = 2
Total memory assigned by malloc = 2 + 2 = 4
First, a suggestion: rewrite
pointer->next=(node*)malloc(sizeof(node));
as
pointer->next = malloc( sizeof *pointer->next );
You don't need the cast (unless you're working on a pre-ANSI implementation, in which case God help you), and using the dereferenced target as the operand of sizeof means you don't have to specify the type, potentially saving you some maintenance heartburn.
Also, a little whitespace goes a long way (although you don't need to put whitespace around the function arguments - that's my style, some people don't like it, but it makes things easier for me to read).
How much bytes of memory is dynamically given to pointer->next
It will be at least as big as sizeof (int) plus sizeof (struct Node *), and potentially may be bigger; depending on your platform, it could be as small as 4 bytes or as large as 16. C allows for "padding" bytes between struct members to satisfy alignment requirements for the underlying architecture. For example, a particular architecture may require that all multi-byte objects be aligned on addresses that are multiples of 4; if your data member is only 2 bytes wide, then there will be 2 unused bytes between it and the next member.
Without knowing a lot about your system, we just can't tell you. You can take that same code and try it on multiple compilers, and you'll get different answers. You have to check yourself, using sizeof(node) or sizeof(struct Node) (I think either syntax works, but just in case).

Size of linked list using the addresses of nodes

The list:
struct list{
int Id;
short Value;
struct list *next;
};
So i want to find out the size of the list(without using sizeof) just by using the adresses of the nodes. Each node differs from one another 24 bytes.This is the code that i have written for this purpose:
struct list *tmp;
int sum=0;
for(tmp=ptr; tmp!=NULL; tmp=tmp->next){
sum = sum + tmp->next - tmp;
}
printf("\nThe size of the list in bytes is:%d", sum);
tmp is a pointer to the list. ptr is a pointer to the list which points to the head. I use tmp because i need ptr later on my code.When i execute the above i get this: The size of the list in bytes is:143132..... The number is different every time, but it is not what i want.
So i want to find out the size of the list(without using sizeof) just by using the adresses of the nodes.
You can't do this. There is no guarantee that adjacent nodes in your list are laid out linearly / without gaps in memory.
he number is different every time
Because memory is potentially allocated at different addresses each run.
Each node differs from one another 24 bytes
Even if memory happened to be allocated adjacently for each node, there can be issues of packing. With a size of 24 bytes that is not likely to happen on common, real computers (because that number is divisible by 4 and 8). However, if your size was e.g. 23, the allocated space would be rounded up on many architectures.
Unless you maintain some sort of separate counter or index, the only way to know the size of a linked list is to traverse it start to end.
Just as complement to Eric, I would say:
As I understand, the question refers to the total number of bytes occupied by the list.
It is sure to use sizeof(list) in order to know the number of bytes occupied by each node. I say "sure" because the result of sizeof(list) is dependent on architecture, especially of the memory alignment and word size.
Now, if for any reason it is forbidden to use sizeof(list), then you could compute it through some such as:
size_t my_sizeof_of_list()
{
struct list * zero = 0;
return (size_t) &zero[1];
}
This code could be adapted to types others than list and inclusive for generic types.
If you know the number of elements of the list, then the total number of bytes is n*sizeof(list) (or n*my_sizeof_of_list()). Otherwise, just as Eric has pointed out, you will need to traverse the list in order to count the nodes. Afterward, the total is n*sizeof(list). In this last case, it is preferable, more simpler and faster, just to count and after compute the total size instead of accumulating it.
find out the size of the list (without using sizeof)
sizelist = (char *)(((struct list *)0)+1)-(char *)(((struct list *)0));
or
#define sizelist ((char *)(((struct list *)0)+1)-(char *)(((struct list *)0)))

Heap memory: Gap of 16 bytes for 8 byte struct

I'm using the following code to create and insert a new node into a linked list, subsequently freeing them.
// the node
struct node
{
int data;
struct node *next;
};
// returns a pointer to a new node with data
struct node *new_node(int data)
{
struct node *node;
node = malloc(sizeof(struct node));
node->data = data;
node->next = NULL;
return node;
}
// insert node at front of list
void push(struct node **head, int data)
{
struct node *node = new_node(data);
node->next = *head;
*head = node;
}
// free the list, deallocate each node's memory
void delete_list(struct node** head)
{
if(*head == NULL)
return;
struct node *next = NULL;
next = (*head)->next;
while(next != NULL)
{
next = (*head)->next;
// print address of the memory released
printf("Freeing %d\n", (int)*head);
free(*head);
*head = next;
}
}
Now, the struct is 8 bytes in my machine (4 byte int and 4 byte pointer). Now, I'm a bit unsure about the following, so please help me out:
When I call push() in sequence, is the memory allocated contiguously? Is that always the case? I guess it cannot be, for the memory in heap can be fragmented.
Say the memory allocated was contiguous, then would it be spaced 8 bytes apart, since the struct's size is 8 bytes. On my machine, when I printed the address of the memory being freed, the memory addresses printed are 16 bytes apart, on every execution. Why?
Freeing 148025480
Freeing 148025464
Freeing 148025448
Freeing 148025432
Freeing 148025416
Freeing 148025400
Freeing 148025384
Freeing 148025368
Freeing 148025352
<empty list>
Now if the memory was NOT allocated contiguously for our integer array (the heap was very much fragmented, and memory requirement was quite large), and we used pointer arithmetic to process each element of the array by incrementing the address by 4 each time(or whatever the size of int is), shouldn't we run into some memory not reserved by our program, throwing off the program? Or is the runtime environment smart enough to take care of this, as the compiler cannot, for it doesn't know how the memory will be allocated. Does the OS take care of this?
Each time you call new_node, it calls malloc().
You cannot (or should not) predict where malloc() will find you memory. It is OS and runtime dependent.
Running on a particular OS, under certain circumstances, you might observe that allocations from consecutive calls to malloc() are contiguous. However that behaviour may change under load, or with a kernel update, a change in the implementation of libc, or under all kinds of other conditions.
You can assume that the chunk of memory allocated by a single call to malloc() is contiguous (at least, in terms of the pointers your program sees). Your program should not assume anything else about contiguity.
If this really bothers you, you can take charge of more of the memory management in your own code -- instead of calling malloc() for each node, call it at the start and get a larger chunk of memory. Subsequent calls to new_node can use part of this chunk. If you run out of space in that chunk, you can either malloc() another chunk (which probably won't be contiguous with the first) or realloc() to extend (and probably move) it.
You'll probably find that all this makes your code more complicated -- and it's up to you whether there are benefits to counter that. The authors of the Hotspot Java VM essentially do this - they malloc() a big block of memory at the start of execution, then rather than call malloc() and free() when the Java program wants memory, it uses its own routines to allocate parts of that block.
Regarding #2, one reason for the results of calls to malloc not being exactly contiguous can be metadata: when you ask for x bytes, malloc implementations may allocate a little extra memory for internal bookkeeping processes (tagging the size of the block, pointers to other free blocks, etc). Thus a request for 8 bytes may actually cause 16 bytes to be allocated internally, hence the 16-byte spacing between successive allocations.
If you allocate memory using malloc, the memory returned will always be contiguous.
If you call malloc twice, there's no guarantee that the two allocations will be placed next to each other, not even if the two malloc calls are right next to each other.
When allocating memory using malloc() you would see a gap of 1-Word size in memory allocations, i.e. 16-Bytes extra would be allocated. This is normal as this is due to the fact that we might want to use free() method for deallocating the memory. The extra space is used by the free() method as a scratch space while performing deallocation operations.
Let say we have the following 2D array:
1 2 3
4 5 6
7 8 9
And the starting address of array is 448 (also the memory address of '1')
Then the memory address for '1', '4', '7' would be 448, 480, 512 respectively which could be calculated as follows:
448 + (3 x 4) + 4 + 16 = 480
Here, we have:
448 - Address of the previous block
3 - Number of columns or number of items in the array
4 - Size of int (I'm assuming the array as integer array)
4 - Extra four bytes because size of int is 4 bytes. So the last int address + 4
16 - As left by the malloc for free() function
I hope it clarifies a little.

Struct size in C [duplicate]

This question already has answers here:
Why isn't sizeof for a struct equal to the sum of sizeof of each member?
(13 answers)
Closed 8 years ago.
I've made a doubly linked structure in C, and need to know how to calculate the size of custom made structures. I understand the size of certain data types, and that pointers are 8 bytes on my machine.
However when I create this data type
struct doublylinked {
int data;
struct doublylinked *next;
struct doublylinked *prev;
};
I get that all the values inside add up to 20 bytes in total.
size of data = 4
size of next = 8
size of prev = 8
However when I print out the size of this data type it equals 24.
size of doublylinked = 24
Where are these extra 4 bytes coming from?
Thanks
The extra space come from the padding added by the compiler, which make access faster on some CPU.
It might actually look like this in memory:
data [4 bytes]
padding [4 bytes] <- That way, next is aligned on a multiple of his own size
next [8 bytes]
prev [8 bytes]
The 8-byte next and prev variables need to have an 8-byte alignment.
In other words, they need to be located in memory addresses divisible by 8.
So the compiler adds a 4-byte padding before or after the 4-byte data variable.
Please note that this is generally platform-dependent (i.e., some processors may support unaligned load/store operations, in which case, the compiler may choose to avoid the padding).

Resources