1+ /*******************************************************************************
2+ Copyright (C) <2024> Intel Corporation
3+
4+ Redistribution and use in source and binary forms, with or without modification,
5+ are permitted provided that the following conditions are met:
6+
7+ 1. Redistributions of source code must retain the above copyright notice,
8+ this list of conditions and the following disclaimer.
9+ 2. Redistributions in binary form must reproduce the above copyright notice,
10+ this list of conditions and the following disclaimer in the documentation
11+ and/or other materials provided with the distribution.
12+ 3. Neither the name of the copyright holder nor the names of its contributors
13+ may be used to endorse or promote products derived from this software
14+ without specific prior written permission.
15+
16+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
20+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
28+
29+ SPDX-License-Identifier: BSD-3-Clause
30+ *******************************************************************************/
31+ #include "pointer_chasing.h"
32+ #include <math.h>
33+ #include <stdint.h>
34+ #include <stdio.h>
35+ #include <stdlib.h>
36+ #include <errno.h>
37+
38+ #define CACHE_LINE_SIZE 64 // Size of a cache line in bytes
39+ #define MINIMAL_NODE_NUMBER 6 // Required for pointer_chase_run_read_write_workload
40+
41+ /**
42+ * @brief Pointer-chase list node. The size of a node is equal to the size of one cache line.
43+ *
44+ */
45+ struct cache_line_node_t
46+ {
47+ union
48+ {
49+ uint8_t buf [CACHE_LINE_SIZE ];
50+ struct
51+ {
52+ cache_line_node_t * next ;
53+ cache_line_node_t * prev ;
54+ };
55+ };
56+ };
57+
58+ static cache_line_node_t * pointer_chasing_init_linear (cache_line_node_t * nodes , size_t n_nodes )
59+ {
60+ for (size_t i = 0 ; i < n_nodes ; i ++ ) {
61+ size_t next_id = (n_nodes + i + 1 ) % n_nodes ;
62+ size_t prev_id = (n_nodes + i - 1 ) % n_nodes ;
63+ nodes [i ].prev = & (nodes [prev_id ]);
64+ nodes [i ].next = & (nodes [next_id ]);
65+ }
66+ return nodes ;
67+ }
68+
69+ static inline void pointer_chasing_swap_lines_far (cache_line_node_t * node1 , cache_line_node_t * node2 )
70+ {
71+ cache_line_node_t * node1_prev = node1 -> prev ;
72+ cache_line_node_t * node1_next = node1 -> next ;
73+ cache_line_node_t * node2_prev = node2 -> prev ;
74+ cache_line_node_t * node2_next = node2 -> next ;
75+ node1 -> prev -> next = node2 ;
76+ node1 -> next -> prev = node2 ;
77+ node2 -> next -> prev = node1 ;
78+ node2 -> prev -> next = node1 ;
79+ node1 -> prev = node2_prev ;
80+ node1 -> next = node2_next ;
81+ node2 -> next = node1_next ;
82+ node2 -> prev = node1_prev ;
83+ }
84+ static inline void pointer_chasing_swap_lines_near (cache_line_node_t * node1 , cache_line_node_t * node2 )
85+ {
86+ cache_line_node_t * node1_prev = node1 -> prev ;
87+ cache_line_node_t * node2_next = node2 -> next ;
88+ node1 -> prev -> next = node2 ;
89+ node2 -> next -> prev = node1 ;
90+ node1 -> prev = node2 ;
91+ node1 -> next = node2_next ;
92+ node2 -> next = node1 ;
93+ node2 -> prev = node1_prev ;
94+ }
95+
96+ static void pointer_chasing_swap_lines (cache_line_node_t * node1 , cache_line_node_t * node2 )
97+ {
98+ if (node1 -> prev == node2 ) {
99+ pointer_chasing_swap_lines_near (node2 , node1 );
100+ } else if (node1 -> next == node2 ) {
101+ pointer_chasing_swap_lines_near (node1 , node2 );
102+ } else if (node1 != node2 ) {
103+ pointer_chasing_swap_lines_far (node1 , node2 );
104+ }
105+ }
106+
107+ static inline cache_line_node_t * pointer_chase_run_read_workload_internal (cache_line_node_t * nodes , size_t n_nodes )
108+ {
109+ while (n_nodes -- ) {
110+ nodes = nodes -> next ;
111+ }
112+ return nodes ;
113+ }
114+
115+ cache_line_node_t * pointer_chase_randomise (cache_line_node_t * nodes ,
116+ size_t n_nodes ,
117+ pointer_chase_random_generator_t generator )
118+ {
119+ for (size_t i = 0 ; i < n_nodes ; i ++ ) {
120+ size_t rand = generator () % n_nodes ;
121+ pointer_chasing_swap_lines (& nodes [i ], & nodes [rand ]);
122+ }
123+ return nodes ;
124+ }
125+ cache_line_node_t * pointer_chase_create_linear (void * buffer , size_t size )
126+ {
127+ size_t n_nodes = size / sizeof (cache_line_node_t );
128+ printf ("Pointer Chasing: Buffer Size %ld\n" , size );
129+ printf ("Pointer Chasing: Number of Nodes %ld\n" , n_nodes );
130+ if (buffer == NULL || n_nodes < MINIMAL_NODE_NUMBER ) {
131+ errno = EINVAL ;
132+ return NULL ;
133+ }
134+ return pointer_chasing_init_linear ((cache_line_node_t * )buffer , n_nodes );
135+ }
136+
137+ cache_line_node_t * pointer_chase_create_random (void * buffer , size_t size , pointer_chase_random_generator_t generator )
138+ {
139+ if (generator == NULL ) {
140+ errno = EINVAL ;
141+ return NULL ;
142+ }
143+ cache_line_node_t * self = pointer_chase_create_linear ((cache_line_node_t * )buffer , size );
144+ if (self == NULL ) {
145+ return NULL ;
146+ }
147+ return pointer_chase_randomise (self , size / sizeof (cache_line_node_t ), generator );
148+ }
149+
150+ __attribute__((optimize ("-O0" ))) cache_line_node_t * pointer_chase_run_read_workload (cache_line_node_t * nodes ,
151+ size_t n_nodes )
152+ {
153+ return pointer_chase_run_read_workload_internal (nodes , n_nodes );
154+ }
155+
156+ __attribute__((optimize ("-O0" ))) cache_line_node_t * pointer_chase_run_workload_read_cyclic (cache_line_node_t * nodes ,
157+ size_t n_cycles )
158+ {
159+ register cache_line_node_t * start = nodes ;
160+ for (size_t cycle = 0 ; cycle < n_cycles ; cycle ++ ) {
161+ while (nodes != start ) {
162+ nodes = nodes -> next ;
163+ }
164+ }
165+ return nodes ;
166+ }
167+
168+ cache_line_node_t * pointer_chase_run_read_write_workload (cache_line_node_t * nodes , size_t n_nodes )
169+ {
170+ while (n_nodes -- ) {
171+ cache_line_node_t * node1 = nodes ;
172+ cache_line_node_t * node2 = pointer_chase_run_read_workload_internal (node1 , 3 );
173+ nodes = pointer_chase_run_read_workload_internal (node2 , 3 );
174+ pointer_chasing_swap_lines_far (node1 , node2 );
175+ }
176+ return nodes ;
177+ }
0 commit comments