1 | // RUN: %libomp-compile -D_GNU_SOURCE |
2 | // RUN: env OMP_PLACES=threads %libomp-run |
3 | // RUN: env OMP_PLACES=cores %libomp-run |
4 | // RUN: env OMP_PLACES=sockets %libomp-run |
5 | // RUN: env OMP_PLACES=cores RUN_OUT_OF_ORDER=1 %libomp-run |
6 | // REQUIRES: linux |
7 | |
8 | #include <stdio.h> |
9 | #include <stdlib.h> |
10 | #include <string.h> |
11 | #include "libomp_test_affinity.h" |
12 | #include "libomp_test_topology.h" |
13 | |
14 | // Check openmp place list to make sure it follow KMP_HW_SUBSET restriction |
15 | static int compare_hw_subset_places(const place_list_t *openmp_places, |
16 | topology_obj_type_t type, int nsockets, |
17 | int ncores_per_socket, |
18 | int nthreads_per_core) { |
19 | int i, j, expected_total, expected_per_place; |
20 | if (type == TOPOLOGY_OBJ_THREAD) { |
21 | expected_total = nsockets * ncores_per_socket * nthreads_per_core; |
22 | expected_per_place = 1; |
23 | } else if (type == TOPOLOGY_OBJ_CORE) { |
24 | expected_total = nsockets * ncores_per_socket; |
25 | expected_per_place = nthreads_per_core; |
26 | } else { |
27 | expected_total = nsockets; |
28 | expected_per_place = ncores_per_socket; |
29 | } |
30 | if (openmp_places->num_places != expected_total) { |
31 | fprintf(stderr, format: "error: KMP_HW_SUBSET did not half each resource layer!\n" ); |
32 | printf(format: "openmp_places places:\n" ); |
33 | topology_print_places(p: openmp_places); |
34 | printf(format: "\n" ); |
35 | return EXIT_FAILURE; |
36 | } |
37 | for (i = 0; i < openmp_places->num_places; ++i) { |
38 | int count = affinity_mask_count(mask: openmp_places->masks[i]); |
39 | if (count != expected_per_place) { |
40 | fprintf(stderr, format: "error: place %d has %d OS procs instead of %d\n" , i, |
41 | count, expected_per_place); |
42 | return EXIT_FAILURE; |
43 | } |
44 | } |
45 | return EXIT_SUCCESS; |
46 | } |
47 | |
48 | static int check_places() { |
49 | char buf[100]; |
50 | topology_obj_type_t type; |
51 | const char *value; |
52 | int status = EXIT_SUCCESS; |
53 | place_list_t *threads, *cores, *sockets, *openmp_places; |
54 | threads = topology_alloc_type_places(type: TOPOLOGY_OBJ_THREAD); |
55 | cores = topology_alloc_type_places(type: TOPOLOGY_OBJ_CORE); |
56 | sockets = topology_alloc_type_places(type: TOPOLOGY_OBJ_SOCKET); |
57 | |
58 | if (threads->num_places <= 1) { |
59 | printf(format: "Only one hardware thread to execute on. Skipping test.\n" ); |
60 | return status; |
61 | } |
62 | |
63 | value = getenv(name: "OMP_PLACES" ); |
64 | if (!value) { |
65 | fprintf(stderr, |
66 | format: "error: OMP_PLACES must be set to one of threads,cores,sockets!\n" ); |
67 | return EXIT_FAILURE; |
68 | } |
69 | if (strcmp(s1: value, s2: "threads" ) == 0) |
70 | type = TOPOLOGY_OBJ_THREAD; |
71 | else if (strcmp(s1: value, s2: "cores" ) == 0) |
72 | type = TOPOLOGY_OBJ_CORE; |
73 | else if (strcmp(s1: value, s2: "sockets" ) == 0) |
74 | type = TOPOLOGY_OBJ_SOCKET; |
75 | else { |
76 | fprintf(stderr, |
77 | format: "error: OMP_PLACES must be one of threads,cores,sockets!\n" ); |
78 | return EXIT_FAILURE; |
79 | } |
80 | |
81 | // Calculate of num threads per core, num cores per socket, & num sockets |
82 | if (cores->num_places <= 0) { |
83 | printf(format: "Invalid number of cores (%d). Skipping test.\n" , cores->num_places); |
84 | return status; |
85 | } else if (sockets->num_places <= 0) { |
86 | printf(format: "Invalid number of sockets (%d). Skipping test.\n" , |
87 | cores->num_places); |
88 | return status; |
89 | } |
90 | int nthreads_per_core = threads->num_places / cores->num_places; |
91 | int ncores_per_socket = cores->num_places / sockets->num_places; |
92 | int nsockets = sockets->num_places; |
93 | |
94 | if (nsockets * ncores_per_socket * nthreads_per_core != threads->num_places) { |
95 | printf(format: "Only uniform topologies can be tested. Skipping test.\n" ); |
96 | return status; |
97 | } |
98 | |
99 | // Use half the resources of every level |
100 | if (nthreads_per_core > 1) |
101 | nthreads_per_core /= 2; |
102 | if (ncores_per_socket > 1) |
103 | ncores_per_socket /= 2; |
104 | if (nsockets > 1) |
105 | nsockets /= 2; |
106 | |
107 | if (getenv(name: "RUN_OUT_OF_ORDER" )) { |
108 | snprintf(s: buf, maxlen: sizeof(buf), format: "%dt,%ds,%dc" , nthreads_per_core, nsockets, |
109 | ncores_per_socket); |
110 | } else { |
111 | snprintf(s: buf, maxlen: sizeof(buf), format: "%ds,%dc,%dt" , nsockets, ncores_per_socket, |
112 | nthreads_per_core); |
113 | } |
114 | setenv(name: "KMP_HW_SUBSET" , value: buf, replace: 1); |
115 | |
116 | openmp_places = topology_alloc_openmp_places(); |
117 | status = compare_hw_subset_places(openmp_places, type, nsockets, |
118 | ncores_per_socket, nthreads_per_core); |
119 | topology_free_places(places: threads); |
120 | topology_free_places(places: cores); |
121 | topology_free_places(places: sockets); |
122 | topology_free_places(places: openmp_places); |
123 | return status; |
124 | } |
125 | |
126 | int main() { |
127 | if (!topology_using_full_mask()) { |
128 | printf(format: "Thread does not have access to all logical processors. Skipping " |
129 | "test.\n" ); |
130 | return EXIT_SUCCESS; |
131 | } |
132 | return check_places(); |
133 | } |
134 | |