illustris 4fb1bd90db
init
2026-01-08 18:11:30 +05:30

176 lines
4.6 KiB
C

/*
* Scenario 4b: Array vs Linked List Traversal
* ============================================
* Arrays have excellent cache locality; linked lists do not.
* This demonstrates why "O(n) vs O(n)" can have very different constants.
*
* Compile: gcc -O2 -o list_vs_array list_vs_array.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 10000000 /* 10 million elements */
struct node {
int value;
struct node *next;
};
double get_time(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec + ts.tv_nsec / 1e9;
}
/* Sum array elements */
long sum_array(int *arr, int n) {
long sum = 0;
for (int i = 0; i < n; i++) {
sum += arr[i];
}
return sum;
}
/* Sum linked list elements */
long sum_list(struct node *head) {
long sum = 0;
struct node *curr = head;
while (curr != NULL) {
sum += curr->value;
curr = curr->next;
}
return sum;
}
/* Create array */
int *create_array(int n) {
int *arr = malloc(n * sizeof(int));
if (!arr) {
perror("malloc array");
exit(1);
}
for (int i = 0; i < n; i++) {
arr[i] = i % 100;
}
return arr;
}
/* Create linked list - nodes allocated sequentially (best case for list) */
struct node *create_list_sequential(int n) {
struct node *nodes = malloc(n * sizeof(struct node));
if (!nodes) {
perror("malloc list");
exit(1);
}
for (int i = 0; i < n - 1; i++) {
nodes[i].value = i % 100;
nodes[i].next = &nodes[i + 1];
}
nodes[n - 1].value = (n - 1) % 100;
nodes[n - 1].next = NULL;
return nodes;
}
/* Create linked list - nodes allocated randomly (worst case for cache) */
struct node *create_list_scattered(int n) {
/* Allocate nodes individually to scatter them in memory */
struct node **nodes = malloc(n * sizeof(struct node *));
if (!nodes) {
perror("malloc");
exit(1);
}
/* Allocate each node separately */
for (int i = 0; i < n; i++) {
nodes[i] = malloc(sizeof(struct node));
if (!nodes[i]) {
perror("malloc node");
exit(1);
}
nodes[i]->value = i % 100;
}
/* Shuffle the order (Fisher-Yates) */
srand(42);
for (int i = n - 1; i > 0; i--) {
int j = rand() % (i + 1);
struct node *tmp = nodes[i];
nodes[i] = nodes[j];
nodes[j] = tmp;
}
/* Link them in shuffled order */
for (int i = 0; i < n - 1; i++) {
nodes[i]->next = nodes[i + 1];
}
nodes[n - 1]->next = NULL;
struct node *head = nodes[0];
free(nodes); /* Free the pointer array, not the nodes */
return head;
}
void free_scattered_list(struct node *head) {
while (head != NULL) {
struct node *next = head->next;
free(head);
head = next;
}
}
int main(void) {
printf("Comparing array vs linked list traversal (%d elements)\n\n", N);
double start, elapsed;
long result;
/* Array */
printf("Creating array...\n");
int *arr = create_array(N);
start = get_time();
result = sum_array(arr, N);
elapsed = get_time() - start;
printf("Array sum: %ld in %.4f seconds\n", result, elapsed);
double array_time = elapsed;
free(arr);
/* Sequential linked list (best case for list) */
printf("\nCreating sequential linked list...\n");
struct node *list_seq = create_list_sequential(N);
start = get_time();
result = sum_list(list_seq);
elapsed = get_time() - start;
printf("List sum (sequential): %ld in %.4f seconds\n", result, elapsed);
double list_seq_time = elapsed;
free(list_seq);
/* Scattered linked list (worst case for cache) */
printf("\nCreating scattered linked list (this takes a while)...\n");
struct node *list_scat = create_list_scattered(N);
start = get_time();
result = sum_list(list_scat);
elapsed = get_time() - start;
printf("List sum (scattered): %ld in %.4f seconds\n", result, elapsed);
double list_scat_time = elapsed;
free_scattered_list(list_scat);
printf("\n--- Summary ---\n");
printf("Array: %.4fs (baseline)\n", array_time);
printf("List (sequential): %.4fs (%.1fx slower)\n",
list_seq_time, list_seq_time / array_time);
printf("List (scattered): %.4fs (%.1fx slower)\n",
list_scat_time, list_scat_time / array_time);
printf("\nTo see cache behavior:\n");
printf(" perf stat -e cache-misses,cache-references ./list_vs_array\n");
return 0;
}