1 //------------------------------------------------------------------------------
2 // GB_context.h: definitions for the internal context
3 //------------------------------------------------------------------------------
4
5 // SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
6 // SPDX-License-Identifier: Apache-2.0
7
8 //------------------------------------------------------------------------------
9
10 #ifndef GB_CONTEXT_H
11 #define GB_CONTEXT_H
12
13 //------------------------------------------------------------------------------
14 // GB_context: error logging, thread control, and Werk space
15 //------------------------------------------------------------------------------
16
17 // Error messages are logged in Context->logger_handle, on the stack which is
18 // handle to the input/output matrix/vector (typically C). If the user-defined
19 // data types, operators, etc have really long names, the error messages are
20 // safely truncated (via snprintf). This is intentional, but gcc with
21 // -Wformat-truncation will print a warning (see pragmas above). Ignore the
22 // warning.
23
24 // The Context also contains the number of threads to use in the operation. It
25 // is normally determined from the user's descriptor, with a default of
26 // nthreads_max = GxB_DEFAULT (that is, zero). The default rule is to let
27 // GraphBLAS determine the number of threads automatically by selecting a
28 // number of threads between 1 and nthreads_max. GrB_init initializes
29 // nthreads_max to omp_get_max_threads. Both the global value and the value in
30 // a descriptor can set/queried by GxB_set / GxB_get.
31
32 // Some GrB_Matrix and GrB_Vector methods do not take a descriptor, however
33 // (GrB_*_dup, _build, _exportTuples, _clear, _nvals, _wait, and GxB_*_resize).
34 // For those methods the default rule is always used (nthreads_max =
35 // GxB_DEFAULT), which then relies on the global nthreads_max.
36
37 // GB_WERK_SIZE is the size of a small fixed-sized array in the Context, used
38 // for small werkspace allocations (typically O(# of threads or # tasks)).
39 // GB_WERK_SIZE must be a multiple of 8. The Werk array is placed first in the
40 // GB_Context struct, to ensure proper alignment.
41
42 #define GB_WERK_SIZE 16384
43
44 typedef struct
45 {
46 GB_void Werk [GB_WERK_SIZE] ; // werkspace stack
47 double chunk ; // chunk size for small problems
48 const char *where ; // GraphBLAS function where error occurred
49 char **logger_handle ; // error report
50 size_t *logger_size_handle ;
51 int nthreads_max ; // max # of threads to use
52 int pwerk ; // top of Werk stack, initially zero
53 }
54 GB_Context_struct ;
55
56 typedef GB_Context_struct *GB_Context ;
57
58 // GB_WHERE keeps track of the currently running user-callable function.
59 // User-callable functions in this implementation are written so that they do
60 // not call other unrelated user-callable functions (except for GrB_*free).
61 // Related user-callable functions can call each other since they all report
62 // the same type-generic name. Internal functions can be called by many
63 // different user-callable functions, directly or indirectly. It would not be
64 // helpful to report the name of an internal function that flagged an error
65 // condition. Thus, each time a user-callable function is entered, it logs the
66 // name of the function with the GB_WHERE macro.
67
68 #define GB_CONTEXT(where_string) \
69 /* construct the Context */ \
70 GB_Context_struct Context_struct ; \
71 GB_Context Context = &Context_struct ; \
72 /* set Context->where so GrB_error can report it if needed */ \
73 Context->where = where_string ; \
74 /* get the default max # of threads and default chunk size */ \
75 Context->nthreads_max = GB_Global_nthreads_max_get ( ) ; \
76 Context->chunk = GB_Global_chunk_get ( ) ; \
77 /* get the pointer to where any error will be logged */ \
78 Context->logger_handle = NULL ; \
79 Context->logger_size_handle = NULL ; \
80 /* initialize the Werk stack */ \
81 Context->pwerk = 0 ;
82
83 // C is a matrix, vector, scalar, or descriptor
84 #define GB_WHERE(C,where_string) \
85 if (!GB_Global_GrB_init_called_get ( )) \
86 { \
87 return (GrB_PANIC) ; /* GrB_init not called */ \
88 } \
89 GB_CONTEXT (where_string) \
90 if (C != NULL) \
91 { \
92 /* free any prior error logged in the object */ \
93 GB_FREE (&(C->logger), C->logger_size) ; \
94 Context->logger_handle = &(C->logger) ; \
95 Context->logger_size_handle = &(C->logger_size) ; \
96 }
97
98 // create the Context, with no error logging
99 #define GB_WHERE1(where_string) \
100 if (!GB_Global_GrB_init_called_get ( )) \
101 { \
102 return (GrB_PANIC) ; /* GrB_init not called */ \
103 } \
104 GB_CONTEXT (where_string)
105
106 //------------------------------------------------------------------------------
107 // GB_GET_NTHREADS_MAX: determine max # of threads for OpenMP parallelism.
108 //------------------------------------------------------------------------------
109
110 // GB_GET_NTHREADS_MAX obtains the max # of threads to use and the chunk
111 // size from the Context. If Context is NULL then a single thread *must*
112 // be used. If Context->nthreads_max is <= GxB_DEFAULT, then select
113 // automatically: between 1 and nthreads_max, depending on the problem
114 // size. Below is the default rule. Any function can use its own rule
115 // instead, based on Context, chunk, nthreads_max, and the problem size.
116 // No rule can exceed nthreads_max.
117
118 #define GB_GET_NTHREADS_MAX(nthreads_max,chunk,Context) \
119 int nthreads_max = (Context == NULL) ? 1 : Context->nthreads_max ; \
120 if (nthreads_max <= GxB_DEFAULT) \
121 { \
122 nthreads_max = GB_Global_nthreads_max_get ( ) ; \
123 } \
124 double chunk = (Context == NULL) ? GxB_DEFAULT : Context->chunk ; \
125 if (chunk <= GxB_DEFAULT) \
126 { \
127 chunk = GB_Global_chunk_get ( ) ; \
128 }
129
130 //------------------------------------------------------------------------------
131 // GB_nthreads: determine # of threads to use for a parallel loop or region
132 //------------------------------------------------------------------------------
133
134 // If work < 2*chunk, then only one thread is used.
135 // else if work < 3*chunk, then two threads are used, and so on.
136
GB_nthreads(double work,double chunk,int nthreads_max)137 static inline int GB_nthreads // return # of threads to use
138 (
139 double work, // total work to do
140 double chunk, // give each thread at least this much work
141 int nthreads_max // max # of threads to use
142 )
143 {
144 work = GB_IMAX (work, 1) ;
145 chunk = GB_IMAX (chunk, 1) ;
146 int64_t nthreads = (int64_t) floor (work / chunk) ;
147 nthreads = GB_IMIN (nthreads, nthreads_max) ;
148 nthreads = GB_IMAX (nthreads, 1) ;
149 return ((int) nthreads) ;
150 }
151
152 //------------------------------------------------------------------------------
153 // error logging
154 //------------------------------------------------------------------------------
155
156 // The GB_ERROR macro logs an error in the logger error string.
157 //
158 // if (i >= nrows)
159 // {
160 // GB_ERROR (GrB_INDEX_OUT_OF_BOUNDS,
161 // "Row index %d out of bounds; must be < %d", i, nrows) ;
162 // }
163 //
164 // The user can then do:
165 //
166 // const char *error ;
167 // GrB_error (&error, A) ;
168 // printf ("%s", error) ;
169
170 GB_PUBLIC // accessed by the MATLAB tests in GraphBLAS/Test only
171 const char *GB_status_code (GrB_Info info) ;
172
173 // maximum size of the error logger string
174 #define GB_LOGGER_LEN 384
175
176 // log an error in the error logger string and return the error
177 #define GB_ERROR(info,format,...) \
178 { \
179 if (Context != NULL) \
180 { \
181 char **logger_handle = Context->logger_handle ; \
182 if (logger_handle != NULL) \
183 { \
184 size_t *logger_size_handle = Context->logger_size_handle ; \
185 (*logger_handle) = GB_MALLOC (GB_LOGGER_LEN+1, char, \
186 logger_size_handle) ; \
187 if ((*logger_handle) != NULL) \
188 { \
189 snprintf ((*logger_handle), GB_LOGGER_LEN, \
190 "GraphBLAS error: %s\nfunction: %s\n" format, \
191 GB_status_code (info), Context->where, __VA_ARGS__) ; \
192 } \
193 } \
194 } \
195 return (info) ; \
196 }
197
198 #endif
199
200