1 //------------------------------------------------------------------------------
2 // GB_Global: global values in GraphBLAS
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 // All Global storage is declared, initialized, and accessed here. The
11 // contents of the GB_Global struct are only accessible to functions in this
12 // file. Global storage is used to keep track of the GraphBLAS mode (blocking
13 // or non-blocking), for pointers to malloc/calloc/realloc/free functions,
14 // global matrix options, and other settings.
15
16 #include "GB_atomics.h"
17
18 //------------------------------------------------------------------------------
19 // Global storage: for all threads in a user application that uses GraphBLAS
20 //------------------------------------------------------------------------------
21
22 typedef struct
23 {
24
25 //--------------------------------------------------------------------------
26 // blocking/non-blocking mode, set by GrB_init
27 //--------------------------------------------------------------------------
28
29 GrB_Mode mode ; // GrB_NONBLOCKING or GrB_BLOCKING
30 bool GrB_init_called ; // true if GrB_init already called
31
32 //--------------------------------------------------------------------------
33 // threading control
34 //--------------------------------------------------------------------------
35
36 int nthreads_max ; // max number of threads to use
37 double chunk ; // chunk size for determining # threads to use
38
39 //--------------------------------------------------------------------------
40 // hypersparsity and CSR/CSC format control
41 //--------------------------------------------------------------------------
42
43 float bitmap_switch [GxB_NBITMAP_SWITCH] ; // default bitmap_switch
44 float hyper_switch ; // default hyper_switch for new matrices
45 bool is_csc ; // default CSR/CSC format for new matrices
46
47 //--------------------------------------------------------------------------
48 // abort function: only used for debugging
49 //--------------------------------------------------------------------------
50
51 void (* abort_function ) (void) ;
52
53 //--------------------------------------------------------------------------
54 // malloc/calloc/realloc/free: memory management functions
55 //--------------------------------------------------------------------------
56
57 // All threads must use the same malloc/calloc/realloc/free functions.
58 // They default to the ANSI C11 functions, but can be defined by GxB_init.
59
60 void * (* malloc_function ) (size_t) ; // required
61 // void * (* calloc_function ) (size_t, size_t) ; // no longer used
62 void * (* realloc_function ) (void *, size_t) ; // may be NULL
63 void (* free_function ) (void *) ; // required
64 bool malloc_is_thread_safe ; // default is true
65
66 //--------------------------------------------------------------------------
67 // memory usage tracking: for testing and debugging only
68 //--------------------------------------------------------------------------
69
70 // malloc_tracking: default is false. There is no user-accessible API for
71 // setting this to true. If true, the following statistics are computed.
72 // If false, all of the following are unused.
73
74 // nmalloc: To aid in searching for memory leaks, GraphBLAS keeps track of
75 // the number of blocks of allocated that have not yet been freed. The
76 // count starts at zero. GB_malloc_memory and GB_calloc_memory increment
77 // this count, and free (of a non-NULL pointer) decrements it. realloc
78 // increments the count it if is allocating a new block, but it does this
79 // by calling GB_malloc_memory.
80
81 // malloc_debug: this is used for testing only (GraphBLAS/Tcov). If true,
82 // then use malloc_debug_count for testing memory allocation and
83 // out-of-memory conditions. If malloc_debug_count > 0, the value is
84 // decremented after each allocation of memory. If malloc_debug_count <=
85 // 0, the GB_*_memory routines pretend to fail; returning NULL and not
86 // allocating anything.
87
88 bool malloc_tracking ; // true if allocations are being tracked
89 int64_t nmalloc ; // number of blocks allocated but not freed
90 bool malloc_debug ; // if true, test memory handling
91 int64_t malloc_debug_count ; // for testing memory handling
92
93 //--------------------------------------------------------------------------
94 // for testing and development
95 //--------------------------------------------------------------------------
96
97 int64_t hack [2] ; // settings for testing/developement only
98
99 //--------------------------------------------------------------------------
100 // diagnostic output
101 //--------------------------------------------------------------------------
102
103 bool burble ; // controls GBURBLE output
104 GB_printf_function_t printf_func ; // pointer to printf
105 GB_flush_function_t flush_func ; // pointer to flush
106
107 //--------------------------------------------------------------------------
108 // for MATLAB interface only
109 //--------------------------------------------------------------------------
110
111 bool print_one_based ; // if true, print 1-based indices
112
113 //--------------------------------------------------------------------------
114 // timing: for code development only
115 //--------------------------------------------------------------------------
116
117 double timing [40] ;
118
119 //--------------------------------------------------------------------------
120 // for malloc debugging only
121 //--------------------------------------------------------------------------
122
123 #ifdef GB_DEBUG
124 #define GB_MEMTABLE_SIZE 10000
125 GB_void *memtable_p [GB_MEMTABLE_SIZE] ;
126 size_t memtable_s [GB_MEMTABLE_SIZE] ;
127 #endif
128 int nmemtable ;
129
130 //--------------------------------------------------------------------------
131 // internal memory pool
132 //--------------------------------------------------------------------------
133
134 // free_pool [k] is a pointer to a link list of freed blocks, all of size
135 // exactly equal to 2^k. The total number of blocks in the kth pool is
136 // given by free_pool_nblocks [k], and the upper bound on this is given by
137 // free_pool_limit [k]. If any additional blocks of size 2^k above that
138 // limit are freed by GB_dealloc_memory, they are not placed in the pool,
139 // but actually freed instead.
140
141 void *free_pool [64] ;
142 int64_t free_pool_nblocks [64] ;
143 int64_t free_pool_limit [64] ;
144
145 //--------------------------------------------------------------------------
146 // CUDA (DRAFT: in progress)
147 //--------------------------------------------------------------------------
148
149 int gpu_count ; // # of GPUs in the system
150 GrB_Desc_Value gpu_control ; // always, never, or default
151 double gpu_chunk ; // min problem size for using a GPU
152 // properties of each GPU:
153 GB_cuda_device gpu_properties [GB_CUDA_MAX_GPUS] ;
154
155 }
156 GB_Global_struct ;
157
158 GB_PUBLIC GB_Global_struct GB_Global ;
159
160 GB_Global_struct GB_Global =
161 {
162
163 // GraphBLAS mode
164 .mode = GrB_NONBLOCKING, // default is nonblocking
165
166 // initialization flag
167 .GrB_init_called = false, // GrB_init has not yet been called
168
169 // max number of threads and chunk size
170 .nthreads_max = 1,
171 .chunk = GB_CHUNK_DEFAULT,
172
173 // min dimension density
174 #define GB_BITSWITCH_1 ((float) 0.04)
175 #define GB_BITSWITCH_2 ((float) 0.05)
176 #define GB_BITSWITCH_3_to_4 ((float) 0.06)
177 #define GB_BITSWITCH_5_to_8 ((float) 0.08)
178 #define GB_BITSWITCH_9_to_16 ((float) 0.10)
179 #define GB_BITSWITCH_17_to_32 ((float) 0.20)
180 #define GB_BITSWITCH_33_to_64 ((float) 0.30)
181 #define GB_BITSWITCH_gt_than_64 ((float) 0.40)
182
183 // default format
184 .bitmap_switch = {
185 GB_BITSWITCH_1,
186 GB_BITSWITCH_2,
187 GB_BITSWITCH_3_to_4,
188 GB_BITSWITCH_5_to_8,
189 GB_BITSWITCH_9_to_16,
190 GB_BITSWITCH_17_to_32,
191 GB_BITSWITCH_33_to_64,
192 GB_BITSWITCH_gt_than_64 },
193 .hyper_switch = GB_HYPER_SWITCH_DEFAULT,
194
195 .is_csc = (GB_FORMAT_DEFAULT != GxB_BY_ROW), // default is GxB_BY_ROW
196
197 // abort function for debugging only
198 .abort_function = abort,
199
200 // malloc/calloc/realloc/free functions: default to ANSI C11 functions
201 .malloc_function = malloc,
202 // .calloc_function = NULL, // no longer used
203 .realloc_function = realloc,
204 .free_function = free,
205 .malloc_is_thread_safe = true,
206
207 // malloc tracking, for testing, statistics, and debugging only
208 .malloc_tracking = false,
209 .nmalloc = 0, // memory block counter
210 .malloc_debug = false, // do not test memory handling
211 .malloc_debug_count = 0, // counter for testing memory handling
212
213 // for testing and development only
214 .hack = {0, 0},
215
216 // diagnostics
217 .burble = false,
218 .printf_func = NULL,
219 .flush_func = NULL,
220
221 // for MATLAB interface only
222 .print_one_based = false, // if true, print 1-based indices
223
224 .timing = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
225 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
226
227 // for malloc debugging only
228 .nmemtable = 0, // memtable is empty
229
230 // all free_pool lists start out empty
231 .free_pool = {
232 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
233 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
234 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
235 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
236 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
237 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
238 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
239 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
240
241 .free_pool_nblocks = {
242 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
243 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
244 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
245 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
246
247 // default limits on the number of free blocks in each list:
248 .free_pool_limit = {
249 0, // size 2^0 = 1 byte none
250 0, // size 2^1 = 2 none
251 0, // size 2^2 = 4 none
252
253 16483, // size 2^3 = 8 (2^14 blocks * 2^3 = 128 KB total)
254 16483, // size 2^4 = 16 bytes (2^14 blocks * 2^4 = 256 KB total)
255 16483, // size 2^5 = 32 (2^14 blocks * 2^5 = 512 KB total)
256 16483, // size 2^6 = 64 (2^14 blocks * 2^6 = 1 MB total)
257 16483, // size 2^7 = 128 (2^14 blocks * 2^7 = 2 MB total)
258
259 16483, // size 2^8 = 256 (2^14 blocks * 2^8 = 4 MB total)
260 8192, // size 2^9 = 512 (2^13 blocks * 2^9 = 4 MB total)
261 4096, // size 2^10 = 1 KB (2^12 blocks * 2^10 = 4 MB total)
262 2048, // size 2^11 = 2 KB (2^11 blocks * 2^11 = 4 MB total)
263
264 1024, // size 2^12 = 4 KB (2^10 blocks * 2^12 = 4 MB total)
265 512, // size 2^13 = 8 KB (2^9 blocks * 2^13 = 4 MB total)
266 256, // size 2^14 = 16 KB (2^8 blocks * 2^14 = 4 MB total)
267 128, // size 2^15 = 32 KB (2^7 blocks * 2^15 = 4 MB total)
268
269 64, // size 2^16 = 64 KB (2^6 blocks * 2^16 = 4 MB total)
270 32, // size 2^17 = 128 KB (2^5 blocks * 2^17 = 4 MB total)
271 16, // size 2^18 = 256 KB (2^4 blocks * 2^18 = 4 MB total)
272 8, // size 2^19 = 512 KB (2^3 blocks * 2^19 = 4 MB total)
273
274 // maximum total size = about 52 MB
275 // by default, no blocks larger than 512 KB are kept in the free_pool
276
277 0, // size 2^20 = 1 MB
278 0, // size 2^21
279 0, // size 2^22
280 0, // size 2^23
281 0, // size 2^24
282 0, // size 2^25
283 0, // size 2^26
284 0, // size 2^27
285 0, // size 2^28
286 0, // size 2^29
287
288 0, // size 2^30 (1 GB)
289 0, // size 2^31
290 0, // size 2^32
291 0, // size 2^33
292 0, // size 2^34
293 0, // size 2^35
294 0, // size 2^36
295 0, // size 2^37
296 0, // size 2^38
297 0, // size 2^39
298
299 // These larger sizes are of course unlikely to appear, but adding all
300 // 64 possibilities means that the free_pool does not need to check an
301 // upper bound.
302
303 0, // size 2^40 (1 TB)
304 0, // size 2^41
305 0, // size 2^42
306 0, // size 2^43
307 0, // size 2^44
308 0, // size 2^45
309 0, // size 2^46
310 0, // size 2^47
311 0, // size 2^48
312 0, // size 2^49
313
314 0, // size 2^50 (1 PB)
315 0, // size 2^51
316 0, // size 2^52
317 0, // size 2^53
318 0, // size 2^54
319 0, // size 2^55
320 0, // size 2^56
321 0, // size 2^57
322 0, // size 2^58
323 0, // size 2^59
324
325 0, // size 2^60 (1 exabyte)
326 0, // size 2^61
327 0, // size 2^62
328 0 }, // size 2^63 (4 exabytes!)
329
330 // CUDA environment (DRAFT: in progress)
331 .gpu_count = 0, // # of GPUs in the system
332 .gpu_control = GxB_DEFAULT, // always, never, or default
333 .gpu_chunk = GB_GPU_CHUNK_DEFAULT, // min problem size for using a GPU
334
335 } ;
336
337 //==============================================================================
338 // GB_Global access functions
339 //==============================================================================
340
341 //------------------------------------------------------------------------------
342 // mode
343 //------------------------------------------------------------------------------
344
GB_Global_mode_set(GrB_Mode mode)345 void GB_Global_mode_set (GrB_Mode mode)
346 {
347 GB_Global.mode = mode ;
348 }
349
GB_Global_mode_get(void)350 GrB_Mode GB_Global_mode_get (void)
351 {
352 return (GB_Global.mode) ;
353 }
354
355 //------------------------------------------------------------------------------
356 // GrB_init_called
357 //------------------------------------------------------------------------------
358
359 GB_PUBLIC
GB_Global_GrB_init_called_set(bool GrB_init_called)360 void GB_Global_GrB_init_called_set (bool GrB_init_called)
361 {
362 GB_Global.GrB_init_called = GrB_init_called ;
363 }
364
365 GB_PUBLIC
GB_Global_GrB_init_called_get(void)366 bool GB_Global_GrB_init_called_get (void)
367 {
368 return (GB_Global.GrB_init_called) ;
369 }
370
371 //------------------------------------------------------------------------------
372 // nthreads_max
373 //------------------------------------------------------------------------------
374
375 GB_PUBLIC
GB_Global_nthreads_max_set(int nthreads_max)376 void GB_Global_nthreads_max_set (int nthreads_max)
377 {
378 GB_Global.nthreads_max = GB_IMAX (nthreads_max, 1) ;
379 }
380
381 GB_PUBLIC
GB_Global_nthreads_max_get(void)382 int GB_Global_nthreads_max_get (void)
383 {
384 return (GB_Global.nthreads_max) ;
385 }
386
387 //------------------------------------------------------------------------------
388 // OpenMP max_threads
389 //------------------------------------------------------------------------------
390
391 GB_PUBLIC
GB_Global_omp_get_max_threads(void)392 int GB_Global_omp_get_max_threads (void)
393 {
394 return (GB_OPENMP_MAX_THREADS) ;
395 }
396
397 //------------------------------------------------------------------------------
398 // chunk
399 //------------------------------------------------------------------------------
400
401 GB_PUBLIC
GB_Global_chunk_set(double chunk)402 void GB_Global_chunk_set (double chunk)
403 {
404 if (chunk <= GxB_DEFAULT) chunk = GB_CHUNK_DEFAULT ;
405 GB_Global.chunk = fmax (chunk, 1) ;
406 }
407
408 GB_PUBLIC
GB_Global_chunk_get(void)409 double GB_Global_chunk_get (void)
410 {
411 return (GB_Global.chunk) ;
412 }
413
414 //------------------------------------------------------------------------------
415 // hyper_switch
416 //------------------------------------------------------------------------------
417
418 GB_PUBLIC
GB_Global_hyper_switch_set(float hyper_switch)419 void GB_Global_hyper_switch_set (float hyper_switch)
420 {
421 GB_Global.hyper_switch = hyper_switch ;
422 }
423
424 GB_PUBLIC
GB_Global_hyper_switch_get(void)425 float GB_Global_hyper_switch_get (void)
426 {
427 return (GB_Global.hyper_switch) ;
428 }
429
430 //------------------------------------------------------------------------------
431 // bitmap_switch
432 //------------------------------------------------------------------------------
433
434 GB_PUBLIC
GB_Global_bitmap_switch_set(int k,float b)435 void GB_Global_bitmap_switch_set (int k, float b)
436 {
437 k = GB_IMAX (k, 0) ;
438 k = GB_IMIN (k, 7) ;
439 GB_Global.bitmap_switch [k] = b ;
440 }
441
442 GB_PUBLIC
GB_Global_bitmap_switch_get(int k)443 float GB_Global_bitmap_switch_get (int k)
444 {
445 k = GB_IMAX (k, 0) ;
446 k = GB_IMIN (k, 7) ;
447 return (GB_Global.bitmap_switch [k]) ;
448 }
449
450 GB_PUBLIC
GB_Global_bitmap_switch_matrix_get(int64_t vlen,int64_t vdim)451 float GB_Global_bitmap_switch_matrix_get (int64_t vlen, int64_t vdim)
452 {
453 int64_t d = GB_IMIN (vlen, vdim) ;
454 if (d <= 1) return (GB_Global.bitmap_switch [0]) ;
455 if (d <= 2) return (GB_Global.bitmap_switch [1]) ;
456 if (d <= 4) return (GB_Global.bitmap_switch [2]) ;
457 if (d <= 8) return (GB_Global.bitmap_switch [3]) ;
458 if (d <= 16) return (GB_Global.bitmap_switch [4]) ;
459 if (d <= 32) return (GB_Global.bitmap_switch [5]) ;
460 if (d <= 64) return (GB_Global.bitmap_switch [6]) ;
461 return (GB_Global.bitmap_switch [7]) ;
462 }
463
464 GB_PUBLIC
GB_Global_bitmap_switch_default(void)465 void GB_Global_bitmap_switch_default (void)
466 {
467 GB_Global.bitmap_switch [0] = GB_BITSWITCH_1 ;
468 GB_Global.bitmap_switch [1] = GB_BITSWITCH_2 ;
469 GB_Global.bitmap_switch [2] = GB_BITSWITCH_3_to_4 ;
470 GB_Global.bitmap_switch [3] = GB_BITSWITCH_5_to_8 ;
471 GB_Global.bitmap_switch [4] = GB_BITSWITCH_9_to_16 ;
472 GB_Global.bitmap_switch [5] = GB_BITSWITCH_17_to_32 ;
473 GB_Global.bitmap_switch [6] = GB_BITSWITCH_33_to_64 ;
474 GB_Global.bitmap_switch [7] = GB_BITSWITCH_gt_than_64 ;
475 }
476
477 //------------------------------------------------------------------------------
478 // is_csc
479 //------------------------------------------------------------------------------
480
GB_Global_is_csc_set(bool is_csc)481 void GB_Global_is_csc_set (bool is_csc)
482 {
483 GB_Global.is_csc = is_csc ;
484 }
485
GB_Global_is_csc_get(void)486 bool GB_Global_is_csc_get (void)
487 {
488 return (GB_Global.is_csc) ;
489 }
490
491 //------------------------------------------------------------------------------
492 // abort_function
493 //------------------------------------------------------------------------------
494
495 GB_PUBLIC
GB_Global_abort_function_set(void (* abort_function)(void))496 void GB_Global_abort_function_set (void (* abort_function) (void))
497 {
498 GB_Global.abort_function = abort_function ;
499 }
500
501 GB_PUBLIC
GB_Global_abort_function(void)502 void GB_Global_abort_function (void)
503 {
504 GB_Global.abort_function ( ) ;
505 }
506
507 //------------------------------------------------------------------------------
508 // malloc debuging
509 //------------------------------------------------------------------------------
510
511 // These functions keep a separate record of the pointers to all allocated
512 // blocks of memory and their sizes, just for sanity checks.
513
514 GB_PUBLIC
GB_Global_memtable_dump(void)515 void GB_Global_memtable_dump (void)
516 {
517 #ifdef GB_DEBUG
518 printf ("\nmemtable dump: %d nmalloc %ld\n", GB_Global.nmemtable,
519 GB_Global.nmalloc) ;
520 for (int k = 0 ; k < GB_Global.nmemtable ; k++)
521 {
522 printf (" %4d: %12p : %ld\n", k,
523 GB_Global.memtable_p [k],
524 GB_Global.memtable_s [k]) ;
525 }
526 #endif
527 }
528
529 GB_PUBLIC
GB_Global_memtable_n(void)530 int GB_Global_memtable_n (void)
531 {
532 return (GB_Global.nmemtable) ;
533 }
534
535 GB_PUBLIC
GB_Global_memtable_clear(void)536 void GB_Global_memtable_clear (void)
537 {
538 GB_Global.nmemtable = 0 ;
539 }
540
541 // add a pointer to the table of malloc'd blocks
542 GB_PUBLIC
GB_Global_memtable_add(void * p,size_t size)543 void GB_Global_memtable_add (void *p, size_t size)
544 {
545 #ifdef GB_DEBUG
546 ASSERT ((p == NULL) == (size == 0)) ;
547 if (p == NULL) return ;
548 bool fail = false ;
549 // printf ("memtable add %p size %ld\n", p, size) ;
550 #pragma omp critical(GB_memtable)
551 {
552 int n = GB_Global.nmemtable ;
553 fail = (n > GB_MEMTABLE_SIZE) ;
554 if (!fail)
555 {
556 for (int i = 0 ; i < n ; i++)
557 {
558 if (p == GB_Global.memtable_p [i])
559 {
560 printf ("\nadd duplicate %p size %ld\n", p, size) ;
561 GB_Global_memtable_dump ( ) ;
562 printf ("Hey %d %p\n", i,p) ;
563 fail = true ;
564 break ;
565 }
566 }
567 }
568 if (!fail && p != NULL)
569 {
570 GB_Global.memtable_p [n] = p ;
571 GB_Global.memtable_s [n] = size ;
572 GB_Global.nmemtable++ ;
573 }
574 }
575 ASSERT (!fail) ;
576 // GB_Global_memtable_dump ( ) ;
577 #endif
578 }
579
580 // get the size of a malloc'd block
581 GB_PUBLIC
GB_Global_memtable_size(void * p)582 size_t GB_Global_memtable_size (void *p)
583 {
584 size_t size = 0 ;
585 #ifdef GB_DEBUG
586 if (p == NULL) return (0) ;
587 bool found = false ;
588 #pragma omp critical(GB_memtable)
589 {
590 int n = GB_Global.nmemtable ;
591 for (int i = 0 ; i < n ; i++)
592 {
593 if (p == GB_Global.memtable_p [i])
594 {
595 size = GB_Global.memtable_s [i] ;
596 found = true ;
597 break ;
598 }
599 }
600 }
601 if (!found)
602 {
603 printf ("\nFAIL: %p not found\n", p) ;
604 GB_Global_memtable_dump ( ) ;
605 ASSERT (0) ;
606 }
607 #endif
608 return (size) ;
609 }
610
611 // test if a malloc'd block is in the table
612 GB_PUBLIC
GB_Global_memtable_find(void * p)613 bool GB_Global_memtable_find (void *p)
614 {
615 bool found = false ;
616 #ifdef GB_DEBUG
617 if (p == NULL) return (false) ;
618 #pragma omp critical(GB_memtable)
619 {
620 int n = GB_Global.nmemtable ;
621 for (int i = 0 ; i < n ; i++)
622 {
623 if (p == GB_Global.memtable_p [i])
624 {
625 found = true ;
626 break ;
627 }
628 }
629 }
630 #endif
631 return (found) ;
632 }
633
634 // remove a pointer from the table of malloc'd blocks
635 GB_PUBLIC
GB_Global_memtable_remove(void * p)636 void GB_Global_memtable_remove (void *p)
637 {
638 #ifdef GB_DEBUG
639 if (p == NULL) return ;
640 bool found = false ;
641 // printf ("memtable remove %p ", p) ;
642 #pragma omp critical(GB_memtable)
643 {
644 int n = GB_Global.nmemtable ;
645 for (int i = 0 ; i < n ; i++)
646 {
647 if (p == GB_Global.memtable_p [i])
648 {
649 // found p in the table; remove it
650 // printf ("size %ld\n", GB_Global.memtable_s [i]) ;
651 GB_Global.memtable_p [i] = GB_Global.memtable_p [n-1] ;
652 GB_Global.memtable_s [i] = GB_Global.memtable_s [n-1] ;
653 GB_Global.nmemtable -- ;
654 found = true ;
655 break ;
656 }
657 }
658 }
659 if (!found)
660 {
661 printf ("remove %p NOT FOUND\n", p) ;
662 GB_Global_memtable_dump ( ) ;
663 }
664 ASSERT (found) ;
665 // GB_Global_memtable_dump ( ) ;
666 #endif
667 }
668
669 //------------------------------------------------------------------------------
670 // malloc_function
671 //------------------------------------------------------------------------------
672
GB_Global_malloc_function_set(void * (* malloc_function)(size_t))673 void GB_Global_malloc_function_set (void * (* malloc_function) (size_t))
674 {
675 GB_Global.malloc_function = malloc_function ;
676 }
677
GB_Global_malloc_function(size_t size)678 void * GB_Global_malloc_function (size_t size)
679 {
680 void *p = NULL ;
681 if (GB_Global.malloc_is_thread_safe)
682 {
683 p = GB_Global.malloc_function (size) ;
684 }
685 else
686 {
687 #pragma omp critical(GB_malloc_protection)
688 {
689 p = GB_Global.malloc_function (size) ;
690 }
691 }
692 #ifdef GB_DEBUG
693 GB_Global_memtable_add (p, size) ;
694 #endif
695 return (p) ;
696 }
697
698 //------------------------------------------------------------------------------
699 // calloc_function: no longer used
700 //------------------------------------------------------------------------------
701
702 // void GB_Global_calloc_function_set (void * (* calloc_function) (size_t, size_t))
703 // {
704 // GB_Global.calloc_function = calloc_function ;
705 // }
706
707 // bool GB_Global_have_calloc_function (void)
708 // {
709 // return (GB_Global.calloc_function != NULL) ;
710 // }
711
712 // void * GB_Global_calloc_function (size_t count, size_t size)
713 // {
714 // void *p = NULL ;
715 // if (GB_Global.malloc_is_thread_safe)
716 // {
717 // p = GB_Global.calloc_function (count, size) ;
718 // }
719 // else
720 // {
721 // #pragma omp critical(GB_malloc_protection)
722 // {
723 // p = GB_Global.calloc_function (count, size) ;
724 // }
725 // }
726 // #ifdef GB_DEBUG
727 // GB_Global_memtable_add (p, count * size) ;
728 // #endif
729 // return (p) ;
730 // }
731
732 //------------------------------------------------------------------------------
733 // realloc_function
734 //------------------------------------------------------------------------------
735
GB_Global_realloc_function_set(void * (* realloc_function)(void *,size_t))736 void GB_Global_realloc_function_set
737 (
738 void * (* realloc_function) (void *, size_t)
739 )
740 {
741 GB_Global.realloc_function = realloc_function ;
742 }
743
GB_Global_have_realloc_function(void)744 bool GB_Global_have_realloc_function (void)
745 {
746 return (GB_Global.realloc_function != NULL) ;
747 }
748
GB_Global_realloc_function(void * p,size_t size)749 void * GB_Global_realloc_function (void *p, size_t size)
750 {
751 void *pnew = NULL ;
752 if (GB_Global.malloc_is_thread_safe)
753 {
754 pnew = GB_Global.realloc_function (p, size) ;
755 }
756 else
757 {
758 #pragma omp critical(GB_malloc_protection)
759 {
760 pnew = GB_Global.realloc_function (p, size) ;
761 }
762 }
763 #ifdef GB_DEBUG
764 if (pnew != NULL)
765 {
766 GB_Global_memtable_remove (p) ;
767 GB_Global_memtable_add (pnew, size) ;
768 }
769 #endif
770 return (pnew) ;
771 }
772
773 //------------------------------------------------------------------------------
774 // free_function
775 //------------------------------------------------------------------------------
776
GB_Global_free_function_set(void (* free_function)(void *))777 void GB_Global_free_function_set (void (* free_function) (void *))
778 {
779 GB_Global.free_function = free_function ;
780 }
781
GB_Global_free_function(void * p)782 void GB_Global_free_function (void *p)
783 {
784 if (GB_Global.malloc_is_thread_safe)
785 {
786 GB_Global.free_function (p) ;
787 }
788 else
789 {
790 #pragma omp critical(GB_malloc_protection)
791 {
792 GB_Global.free_function (p) ;
793 }
794 }
795 #ifdef GB_DEBUG
796 GB_Global_memtable_remove (p) ;
797 #endif
798 }
799
800 //------------------------------------------------------------------------------
801 // malloc_is_thread_safe
802 //------------------------------------------------------------------------------
803
804 GB_PUBLIC
GB_Global_malloc_is_thread_safe_set(bool malloc_is_thread_safe)805 void GB_Global_malloc_is_thread_safe_set (bool malloc_is_thread_safe)
806 {
807 GB_Global.malloc_is_thread_safe = malloc_is_thread_safe ;
808 }
809
810 GB_PUBLIC
GB_Global_malloc_is_thread_safe_get(void)811 bool GB_Global_malloc_is_thread_safe_get (void)
812 {
813 return (GB_Global.malloc_is_thread_safe) ;
814 }
815
816 //------------------------------------------------------------------------------
817 // malloc_tracking
818 //------------------------------------------------------------------------------
819
820 GB_PUBLIC
GB_Global_malloc_tracking_set(bool malloc_tracking)821 void GB_Global_malloc_tracking_set (bool malloc_tracking)
822 {
823 GB_Global.malloc_tracking = malloc_tracking ;
824 }
825
GB_Global_malloc_tracking_get(void)826 bool GB_Global_malloc_tracking_get (void)
827 {
828 return (GB_Global.malloc_tracking) ;
829 }
830
831 //------------------------------------------------------------------------------
832 // nmalloc
833 //------------------------------------------------------------------------------
834
GB_Global_nmalloc_clear(void)835 void GB_Global_nmalloc_clear (void)
836 {
837 GB_ATOMIC_WRITE
838 GB_Global.nmalloc = 0 ;
839 }
840
841 GB_PUBLIC
GB_Global_nmalloc_get(void)842 int64_t GB_Global_nmalloc_get (void)
843 {
844 int64_t nmalloc ;
845 GB_ATOMIC_READ
846 nmalloc = GB_Global.nmalloc ;
847 return (nmalloc) ;
848 }
849
GB_Global_nmalloc_increment(void)850 void GB_Global_nmalloc_increment (void)
851 {
852 GB_ATOMIC_UPDATE
853 GB_Global.nmalloc++ ;
854 }
855
856 GB_PUBLIC
GB_Global_nmalloc_decrement(void)857 void GB_Global_nmalloc_decrement (void)
858 {
859 GB_ATOMIC_UPDATE
860 GB_Global.nmalloc-- ;
861 }
862
863 //------------------------------------------------------------------------------
864 // malloc_debug
865 //------------------------------------------------------------------------------
866
867 GB_PUBLIC
GB_Global_malloc_debug_set(bool malloc_debug)868 void GB_Global_malloc_debug_set (bool malloc_debug)
869 {
870 GB_ATOMIC_WRITE
871 GB_Global.malloc_debug = malloc_debug ;
872 }
873
GB_Global_malloc_debug_get(void)874 bool GB_Global_malloc_debug_get (void)
875 {
876 bool malloc_debug ;
877 GB_ATOMIC_READ
878 malloc_debug = GB_Global.malloc_debug ;
879 return (malloc_debug) ;
880 }
881
882 //------------------------------------------------------------------------------
883 // malloc_debug_count
884 //------------------------------------------------------------------------------
885
886 GB_PUBLIC
GB_Global_malloc_debug_count_set(int64_t malloc_debug_count)887 void GB_Global_malloc_debug_count_set (int64_t malloc_debug_count)
888 {
889 GB_ATOMIC_WRITE
890 GB_Global.malloc_debug_count = malloc_debug_count ;
891 }
892
GB_Global_malloc_debug_count_decrement(void)893 bool GB_Global_malloc_debug_count_decrement (void)
894 {
895 GB_ATOMIC_UPDATE
896 GB_Global.malloc_debug_count-- ;
897
898 int64_t malloc_debug_count ;
899 GB_ATOMIC_READ
900 malloc_debug_count = GB_Global.malloc_debug_count ;
901 return (malloc_debug_count <= 0) ;
902 }
903
904 //------------------------------------------------------------------------------
905 // hack: for setting an internal flag for testing and development only
906 //------------------------------------------------------------------------------
907
908 GB_PUBLIC
GB_Global_hack_set(int k,int64_t hack)909 void GB_Global_hack_set (int k, int64_t hack)
910 {
911 GB_Global.hack [k] = hack ;
912 }
913
914 GB_PUBLIC
GB_Global_hack_get(int k)915 int64_t GB_Global_hack_get (int k)
916 {
917 return (GB_Global.hack [k]) ;
918 }
919
920 //------------------------------------------------------------------------------
921 // burble: for controlling the burble output
922 //------------------------------------------------------------------------------
923
GB_Global_burble_set(bool burble)924 void GB_Global_burble_set (bool burble)
925 {
926 GB_Global.burble = burble ;
927 }
928
929 GB_PUBLIC
GB_Global_burble_get(void)930 bool GB_Global_burble_get (void)
931 {
932 return (GB_Global.burble) ;
933 }
934
935 GB_PUBLIC
GB_Global_printf_get()936 GB_printf_function_t GB_Global_printf_get ( )
937 {
938 return (GB_Global.printf_func) ;
939 }
940
941 GB_PUBLIC
GB_Global_flush_get()942 GB_flush_function_t GB_Global_flush_get ( )
943 {
944 return (GB_Global.flush_func) ;
945 }
946
947 GB_PUBLIC
GB_Global_printf_set(GB_printf_function_t pr_func)948 void GB_Global_printf_set (GB_printf_function_t pr_func)
949 {
950 GB_Global.printf_func = pr_func ;
951 }
952
953 GB_PUBLIC
GB_Global_flush_set(GB_flush_function_t fl_func)954 void GB_Global_flush_set (GB_flush_function_t fl_func)
955 {
956 GB_Global.flush_func = fl_func ;
957 }
958
959 //------------------------------------------------------------------------------
960 // for MATLAB interface only
961 //------------------------------------------------------------------------------
962
963 GB_PUBLIC
GB_Global_print_one_based_set(bool onebased)964 void GB_Global_print_one_based_set (bool onebased)
965 {
966 GB_Global.print_one_based = onebased ;
967 }
968
969 GB_PUBLIC
GB_Global_print_one_based_get(void)970 bool GB_Global_print_one_based_get (void)
971 {
972 return (GB_Global.print_one_based) ;
973 }
974
975 //------------------------------------------------------------------------------
976 // CUDA (DRAFT: in progress)
977 //------------------------------------------------------------------------------
978
GB_Global_gpu_control_set(GrB_Desc_Value gpu_control)979 void GB_Global_gpu_control_set (GrB_Desc_Value gpu_control)
980 {
981 // set the GPU control to always, never, or default
982 if (GB_Global.gpu_count > 0)
983 {
984 // one or more GPUs are available: set gpu_control to
985 // always, never, or default.
986 if (gpu_control == GxB_GPU_ALWAYS || gpu_control == GxB_GPU_NEVER)
987 {
988 GB_Global.gpu_control = gpu_control ;
989 }
990 else
991 {
992 GB_Global.gpu_control = GxB_DEFAULT ;
993 }
994 }
995 else
996 {
997 // no GPUs available: never use a GPU
998 GB_Global.gpu_control = GxB_GPU_NEVER ;
999 }
1000 }
1001
GB_Global_gpu_control_get(void)1002 GrB_Desc_Value GB_Global_gpu_control_get (void)
1003 {
1004 // get the GPU control parameter
1005 return (GB_Global.gpu_control) ;
1006 }
1007
GB_Global_gpu_chunk_set(double gpu_chunk)1008 void GB_Global_gpu_chunk_set (double gpu_chunk)
1009 {
1010 // set the GPU chunk factor
1011 if (gpu_chunk < 1) gpu_chunk = GB_GPU_CHUNK_DEFAULT ;
1012 GB_Global.gpu_chunk = gpu_chunk ;
1013 }
1014
GB_Global_gpu_chunk_get(void)1015 double GB_Global_gpu_chunk_get (void)
1016 {
1017 // get the GPU chunk factor
1018 return (GB_Global.gpu_chunk) ;
1019 }
1020
GB_Global_gpu_count_set(bool enable_cuda)1021 bool GB_Global_gpu_count_set (bool enable_cuda)
1022 {
1023 // set the # of GPUs in the system;
1024 // this function is only called once, by GB_init.
1025 #if defined ( GBCUDA )
1026 if (enable_cuda)
1027 {
1028 return (GB_cuda_get_device_count (&GB_Global.gpu_count)) ;
1029 }
1030 else
1031 #endif
1032 {
1033 // no GPUs available, or available but not requested
1034 GB_Global.gpu_count = 0 ;
1035 return (true) ;
1036 }
1037 }
1038
GB_Global_gpu_count_get(void)1039 int GB_Global_gpu_count_get (void)
1040 {
1041 // get the # of GPUs in the system
1042 return (GB_Global.gpu_count) ;
1043 }
1044
1045 #define GB_GPU_DEVICE_CHECK(error) \
1046 if (device < 0 || device >= GB_Global.gpu_count) return (error) ;
1047
GB_Global_gpu_memorysize_get(int device)1048 size_t GB_Global_gpu_memorysize_get (int device)
1049 {
1050 // get the memory size of a specific GPU
1051 GB_GPU_DEVICE_CHECK (0) ; // memory size zero if invalid GPU
1052 return (GB_Global.gpu_properties [device].total_global_memory) ;
1053 }
1054
GB_Global_gpu_sm_get(int device)1055 int GB_Global_gpu_sm_get (int device)
1056 {
1057 // get the # of SMs in a specific GPU
1058 GB_GPU_DEVICE_CHECK (0) ; // zero if invalid GPU
1059 return (GB_Global.gpu_properties [device].number_of_sms) ;
1060 }
1061
GB_Global_gpu_device_pool_size_set(int device,size_t size)1062 bool GB_Global_gpu_device_pool_size_set( int device, size_t size)
1063 {
1064 GB_GPU_DEVICE_CHECK (0) ; // zero if invalid GPU
1065 GB_Global.gpu_properties [device].pool_size = (int) size ;
1066 return( true);
1067 }
1068
GB_Global_gpu_device_max_pool_size_set(int device,size_t size)1069 bool GB_Global_gpu_device_max_pool_size_set( int device, size_t size)
1070 {
1071 GB_GPU_DEVICE_CHECK (0) ; // zero if invalid GPU
1072 GB_Global.gpu_properties[device].max_pool_size = (int) size ;
1073 return( true);
1074 }
1075
GB_Global_gpu_device_memory_resource_set(int device,void * resource)1076 bool GB_Global_gpu_device_memory_resource_set( int device, void *resource)
1077 {
1078 GB_GPU_DEVICE_CHECK (0) ; // zero if invalid GPU
1079 GB_Global.gpu_properties[device].memory_resource = resource;
1080 return( true);
1081 }
1082
GB_Global_gpu_device_memory_resource_get(int device)1083 void* GB_Global_gpu_device_memory_resource_get( int device )
1084 {
1085 GB_GPU_DEVICE_CHECK (0) ; // zero if invalid GPU
1086 return ( GB_Global.gpu_properties [device].memory_resource ) ;
1087 //NOTE: this returns a void*, needs to be cast to be used
1088 }
1089
GB_Global_gpu_device_properties_get(int device)1090 bool GB_Global_gpu_device_properties_get (int device)
1091 {
1092 // get all properties of a specific GPU;
1093 // this function is only called once per GPU, by GB_init.
1094 GB_GPU_DEVICE_CHECK (false) ; // fail if invalid GPU
1095 #if defined ( GBCUDA )
1096 return (GB_cuda_get_device_properties (device,
1097 &(GB_Global.gpu_properties [device]))) ;
1098 #else
1099 // if no GPUs exist, they cannot be queried
1100 return (false) ;
1101 #endif
1102 }
1103
1104 //------------------------------------------------------------------------------
1105 // timing: for code development only
1106 //------------------------------------------------------------------------------
1107
1108 GB_PUBLIC
GB_Global_timing_clear_all(void)1109 void GB_Global_timing_clear_all (void)
1110 {
1111 for (int k = 0 ; k < 40 ; k++)
1112 {
1113 GB_Global.timing [k] = 0 ;
1114 }
1115 }
1116
1117 GB_PUBLIC
GB_Global_timing_clear(int k)1118 void GB_Global_timing_clear (int k)
1119 {
1120 GB_Global.timing [k] = 0 ;
1121 }
1122
1123 GB_PUBLIC
GB_Global_timing_set(int k,double t)1124 void GB_Global_timing_set (int k, double t)
1125 {
1126 GB_Global.timing [k] = t ;
1127 }
1128
1129 GB_PUBLIC
GB_Global_timing_add(int k,double t)1130 void GB_Global_timing_add (int k, double t)
1131 {
1132 GB_Global.timing [k] += t ;
1133 }
1134
1135 GB_PUBLIC
GB_Global_timing_get(int k)1136 double GB_Global_timing_get (int k)
1137 {
1138 return (GB_Global.timing [k]) ;
1139 }
1140
1141 //------------------------------------------------------------------------------
1142 // free_pool: fast access to free memory blocks
1143 //------------------------------------------------------------------------------
1144
1145 // each free block contains a pointer to the next free block. This requires
1146 // the free block to be at least 8 bytes in size.
1147 #define GB_NEXT(p) ((void **) p) [0]
1148
1149 // free_pool_init: initialize the free_pool
1150 GB_PUBLIC
GB_Global_free_pool_init(bool clear)1151 void GB_Global_free_pool_init (bool clear)
1152 {
1153 #pragma omp critical(GB_free_pool)
1154 {
1155 if (clear)
1156 {
1157 // clear the free pool
1158 for (int k = 0 ; k < 64 ; k++)
1159 {
1160 GB_Global.free_pool [k] = NULL ;
1161 GB_Global.free_pool_nblocks [k] = 0 ;
1162 }
1163 }
1164 // set the default free_pool_limit
1165 for (int k = 0 ; k < 64 ; k++)
1166 {
1167 GB_Global.free_pool_limit [k] = 0 ;
1168 }
1169 int64_t n = 16384 ;
1170 for (int k = 3 ; k <= 8 ; k++)
1171 {
1172 GB_Global.free_pool_limit [k] = n ;
1173 }
1174 for (int k = 9 ; k <= 19 ; k++)
1175 {
1176 n = n/2 ;
1177 GB_Global.free_pool_limit [k] = n ;
1178 }
1179 }
1180 }
1181
1182 #ifdef GB_DEBUG
1183 // check if a block is valid
GB_Global_free_pool_check(void * p,int k,char * where)1184 static inline void GB_Global_free_pool_check (void *p, int k, char *where)
1185 {
1186 // check the size of the block
1187 // printf ("check %p\n", p) ;
1188 ASSERT (k >= 3 && k < 64) ;
1189 ASSERT (p != NULL) ;
1190 size_t size = GB_Global_memtable_size (p) ;
1191 ASSERT (size == ((size_t) 1) << k) ;
1192 }
1193 #endif
1194
1195 // free_pool_get: get a block from the free_pool, or return NULL if none
1196 GB_PUBLIC
GB_Global_free_pool_get(int k)1197 void *GB_Global_free_pool_get (int k)
1198 {
1199 void *p = NULL ;
1200 ASSERT (k >= 3 && k < 64) ;
1201 #pragma omp critical(GB_free_pool)
1202 {
1203 p = GB_Global.free_pool [k] ;
1204 if (p != NULL)
1205 {
1206 // remove the block from the kth free_pool
1207 GB_Global.free_pool_nblocks [k]-- ;
1208 GB_Global.free_pool [k] = GB_NEXT (p) ;
1209 }
1210 }
1211 if (p != NULL)
1212 {
1213 // clear the next pointer inside the block, since the block needs
1214 // to be all zero
1215 // printf ("got %p k %d\n", p, k) ;
1216 #ifdef GB_DEBUG
1217 GB_Global_free_pool_check (p, k, "get") ;
1218 #endif
1219 // GB_Global_free_pool_dump (2) ; printf ("\ndid get\n\n") ;
1220 }
1221 return (p) ;
1222 }
1223
1224 // free_pool_put: put a block in the free_pool, unless it is full
1225 GB_PUBLIC
GB_Global_free_pool_put(void * p,int k)1226 bool GB_Global_free_pool_put (void *p, int k)
1227 {
1228 #ifdef GB_DEBUG
1229 GB_Global_free_pool_check (p, k, "put") ;
1230 #endif
1231 bool returned_to_pool = false ;
1232 #pragma omp critical(GB_free_pool)
1233 {
1234 returned_to_pool =
1235 (GB_Global.free_pool_nblocks [k] < GB_Global.free_pool_limit [k]) ;
1236 if (returned_to_pool)
1237 {
1238 // add the block to the head of the free_pool list
1239 // printf ("put %p k %d\n", p, k) ;
1240 GB_Global.free_pool_nblocks [k]++ ;
1241 GB_NEXT (p) = GB_Global.free_pool [k] ;
1242 GB_Global.free_pool [k] = p ;
1243 }
1244 }
1245 // GB_Global_free_pool_dump (2) ; printf ("\ndid put\n\n") ;
1246 return (returned_to_pool) ;
1247 }
1248
1249 // free_pool_dump: check the validity of the free_pool
1250 GB_PUBLIC
GB_Global_free_pool_dump(int pr)1251 void GB_Global_free_pool_dump (int pr)
1252 {
1253 #ifdef GB_DEBUG
1254 bool fail = false ;
1255 #pragma omp critical(GB_free_pool)
1256 {
1257 for (int k = 0 ; k < 64 && !fail ; k++)
1258 {
1259 int64_t nblocks = GB_Global.free_pool_nblocks [k] ;
1260 int64_t limit = GB_Global.free_pool_limit [k] ;
1261 if (nblocks != 0 && pr > 0)
1262 {
1263 printf ("pool %2d: %8ld blocks, %8ld limit\n",
1264 k, nblocks, limit) ;
1265 }
1266 int64_t nblocks_actual = 0 ;
1267 void *p = GB_Global.free_pool [k] ;
1268 for ( ; p != NULL && !fail ; p = GB_NEXT (p))
1269 {
1270 if (pr > 1) printf (" %16p ", p) ;
1271 size_t size = GB_Global_memtable_size (p) ;
1272 if (pr > 1) printf ("size: %ld\n", size) ;
1273 nblocks_actual++ ;
1274 fail = fail || (size != ((size_t) 1) << k) ;
1275 if (fail && pr > 0) printf (" fail\n") ;
1276 fail = fail || (nblocks_actual > nblocks) ;
1277 }
1278 if (nblocks_actual != nblocks)
1279 {
1280 if (pr > 0) printf ("fail: # blocks %ld %ld\n",
1281 nblocks_actual, nblocks) ;
1282 fail = true ;
1283 }
1284 }
1285 }
1286 ASSERT (!fail) ;
1287 #endif
1288 }
1289
1290 // free_pool_limit_get: get the limit on the # of blocks in the kth pool
1291 GB_PUBLIC
GB_Global_free_pool_limit_get(int k)1292 int64_t GB_Global_free_pool_limit_get (int k)
1293 {
1294 int64_t nblocks = 0 ;
1295 if (k >= 3 && k < 64)
1296 {
1297 #pragma omp critical(GB_free_pool)
1298 {
1299 nblocks = GB_Global.free_pool_limit [k] ;
1300 }
1301 }
1302 return (nblocks) ;
1303 }
1304
1305 // free_pool_limit_set: set the limit on the # of blocks in the kth pool
1306 GB_PUBLIC
GB_Global_free_pool_limit_set(int k,int64_t nblocks)1307 void GB_Global_free_pool_limit_set (int k, int64_t nblocks)
1308 {
1309 if (k >= 3 && k < 64)
1310 {
1311 #pragma omp critical(GB_free_pool)
1312 {
1313 GB_Global.free_pool_limit [k] = nblocks ;
1314 }
1315 }
1316 }
1317
1318 // free_pool_nblocks_total: total # of blocks in free_pool (for debug only)
1319 GB_PUBLIC
GB_Global_free_pool_nblocks_total(void)1320 int64_t GB_Global_free_pool_nblocks_total (void)
1321 {
1322 int64_t nblocks = 0 ;
1323 #pragma omp critical(GB_free_pool)
1324 {
1325 for (int k = 0 ; k < 64 ; k++)
1326 {
1327 nblocks += GB_Global.free_pool_nblocks [k] ;
1328 }
1329 }
1330 return (nblocks) ;
1331 }
1332
1333