(git:b279b6b)
callgraph.F
Go to the documentation of this file.
1 !--------------------------------------------------------------------------------------------------!
2 ! CP2K: A general program to perform molecular dynamics simulations !
3 ! Copyright 2000-2024 CP2K developers group <https://cp2k.org> !
4 ! !
5 ! SPDX-License-Identifier: GPL-2.0-or-later !
6 !--------------------------------------------------------------------------------------------------!
7 MODULE callgraph
8  USE kinds, ONLY: int_4, int_8
9  USE timings_base_type, ONLY: call_stat_type
10 #include "../base/base_uses.f90"
11 
12  IMPLICIT NONE
13  PRIVATE
14 
15 
16 ! **************************************************************************************************
17 !> \brief A hash map (also known as hashtable or dictionary).
18 !> Internally the hash map uses an array to holds its data.
19 !> If this array reaches a load-factor of 75%, a new array with twice the
20 !> size will be allocated and the items are then copied over.
21 !> This ensures that the dictionary will perform operations in O(1).
22 !> \par History
23 !> 12.2012 first version [Ole Schuett]
24 !> 08.2019 refactored for Fypp [Ole Schuett]
25 !> \author Ole Schuett
26 ! ***************************************************************************************************
27 
28  PUBLIC :: callgraph_init
29  PUBLIC :: callgraph_items
30  PUBLIC :: callgraph_haskey
31  PUBLIC :: callgraph_set
32  PUBLIC :: callgraph_get
33  PUBLIC :: callgraph_size
34  PUBLIC :: callgraph_destroy
35  PUBLIC :: callgraph_type
36  PUBLIC :: callgraph_item_type
37 
38 !this is an internal type
39 !Calculating hashes might be expensive, therefore they are stored
40 !for use during change_capacity().
41  TYPE private_item_type
42  PRIVATE
43  INTEGER(kind=int_4), DIMENSION(2) :: key = 0_int_4
44  TYPE(call_stat_type), POINTER :: value => null()
45  INTEGER(KIND=int_8) :: hash = 0_int_8
46  TYPE(private_item_type), POINTER :: next => null()
47  END TYPE private_item_type
48 
49 !this is an internal type
50  TYPE private_item_p_type
51  PRIVATE
52  TYPE(private_item_type), POINTER :: p => null()
53  END TYPE private_item_p_type
54 
55 ! this is the public type, which holds a hash map instance
56  TYPE callgraph_type
57  PRIVATE
58  TYPE(private_item_p_type), DIMENSION(:), POINTER :: buckets => null()
59  INTEGER :: size = -1
60  END TYPE callgraph_type
61 
62 ! this is a public type, its returned by callgraph_items()
63  TYPE callgraph_item_type
64  INTEGER(kind=int_4), DIMENSION(2) :: key = 0_int_4
65  TYPE(call_stat_type), POINTER :: value => null()
66  END TYPE callgraph_item_type
67 
68  CONTAINS
69 
70 
71 ! **************************************************************************************************
72 !> \brief Allocates the internal data-structures of the given hash map.
73 !> \param hash_map ...
74 !> \param initial_capacity The initial size of the internal array (default=11).
75 !> \author Ole Schuett
76 ! **************************************************************************************************
77  SUBROUTINE callgraph_init(hash_map, initial_capacity)
78  TYPE(callgraph_type), INTENT(inout) :: hash_map
79  INTEGER, INTENT(in), OPTIONAL :: initial_capacity
80 
81  INTEGER :: initial_capacity_
82 
83  IF (PRESENT(initial_capacity)) THEN
84  initial_capacity_ = initial_capacity
85  ELSE
86  initial_capacity_ = 11
87  END IF
88 
89  IF (initial_capacity_ < 1) &
90  cpabort("initial_capacity < 1")
91 
92  IF (ASSOCIATED(hash_map%buckets)) &
93  cpabort("hash map is already initialized.")
94 
95  ALLOCATE (hash_map%buckets(initial_capacity_))
96  hash_map%size = 0
97 
98  END SUBROUTINE callgraph_init
99 
100 ! **************************************************************************************************
101 !> \brief Test if the given hash map has been initialized.
102 !> \param hash_map ...
103 !> \return ...
104 !> \author Ole Schuett
105 ! **************************************************************************************************
106  FUNCTION callgraph_isready(hash_map) RESULT(res)
107  TYPE(callgraph_type), INTENT(inout) :: hash_map
108  LOGICAL :: res
109  res = ASSOCIATED(hash_map%buckets)
110  END FUNCTION callgraph_isready
111 
112 ! **************************************************************************************************
113 !> \brief Deallocated the internal data-structures if the given hash map.
114 !> Caution: If the stored keys or values are pointers, their targets will
115 !> not get deallocated by this routine.
116 !> \param hash_map ...
117 !> \author Ole Schuett
118 ! **************************************************************************************************
119  SUBROUTINE callgraph_destroy(hash_map)
120  TYPE(callgraph_type), INTENT(inout) :: hash_map
121  TYPE(private_item_type), POINTER :: item, prev_item
122  INTEGER :: i
123 
124  cpassert(ASSOCIATED(hash_map%buckets))
125 
126  DO i = 1, size(hash_map%buckets)
127  item => hash_map%buckets(i)%p
128  DO WHILE (ASSOCIATED(item))
129  prev_item => item
130  item => item%next
131  DEALLOCATE (prev_item)
132  END DO
133  END DO
134 
135  DEALLOCATE (hash_map%buckets)
136  hash_map%size = -1
137  END SUBROUTINE callgraph_destroy
138 
139 ! **************************************************************************************************
140 !> \brief Stores, and possibly overwrites, a given value under a given key.
141 !> \param hash_map ...
142 !> \param key ...
143 !> \param value ...
144 !> \author Ole Schuett
145 ! **************************************************************************************************
146  SUBROUTINE callgraph_set(hash_map, key, value)
147  TYPE(callgraph_type), INTENT(inout) :: hash_map
148  INTEGER(kind=int_4), DIMENSION(2), INTENT(in) :: key
149  TYPE(call_stat_type), POINTER, INTENT(in) :: value
150  INTEGER(KIND=int_8) :: hash
151  cpassert(ASSOCIATED(hash_map%buckets))
152 
153  hash = callgraph_hash_function(key)
154  CALL callgraph_set_hashed(hash_map, key, value, hash)
155  END SUBROUTINE callgraph_set
156 
157 ! **************************************************************************************************
158 !> \brief Common code used internally by callgraph_set() and callgraph_change_capacity().
159 !> \param hash_map ...
160 !> \param key ...
161 !> \param value ...
162 !> \param hash ...
163 !> \author Ole Schuett
164 ! **************************************************************************************************
165  RECURSIVE SUBROUTINE callgraph_set_hashed(hash_map, key, value, hash)
166  TYPE(callgraph_type), INTENT(inout) :: hash_map
167  INTEGER(kind=int_4), DIMENSION(2), intent(in) :: key
168  TYPE(call_stat_type), POINTER, intent(in) :: value
169  INTEGER(KIND=int_8), intent(in) :: hash
170  TYPE(private_item_type), POINTER :: item, new_item
171  INTEGER(KIND=int_8) :: idx
172 
173  idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
174 
175  ! if already in hash map just update its value
176  item => hash_map%buckets(idx)%p
177  DO WHILE (ASSOCIATED(item))
178  IF (item%hash == hash) THEN
179  IF (callgraph_keys_equal(item%key, key)) THEN
180  item%value =>value
181  RETURN
182  END IF
183  END IF
184  item => item%next
185  END DO
186 
187  ! check load-factor
188  IF (4*hash_map%size > 3*size(hash_map%buckets)) THEN ! load-factor > 75%
189  call callgraph_change_capacity(hash_map, 2*size(hash_map%buckets)) !double capacity
190  idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
191  END IF
192 
193  ! create a new item
194  allocate (new_item)
195  new_item%hash = hash
196  new_item%key =key
197  new_item%value =>value
198  new_item%next => hash_map%buckets(idx)%p
199  hash_map%buckets(idx)%p => new_item
200  hash_map%size = hash_map%size + 1
201 
202  END SUBROUTINE callgraph_set_hashed
203 
204 ! **************************************************************************************************
205 !> \brief Internal routine for changing the hash map's capacity.
206 !> \param hash_map ...
207 !> \param new_capacity ...
208 !> \author Ole Schuett
209 ! **************************************************************************************************
210  RECURSIVE SUBROUTINE callgraph_change_capacity(hash_map, new_capacity)
211  TYPE(callgraph_type), INTENT(inout) :: hash_map
212  INTEGER, INTENT(in) :: new_capacity
213  INTEGER :: i, old_size, new_cap
214  TYPE(private_item_type), POINTER :: item, prev_item
215  TYPE(private_item_p_type), DIMENSION(:), POINTER :: old_buckets
216  new_cap = new_capacity
217  ! pre checks
218  IF (new_cap > huge(i)) THEN
219  IF (size(hash_map%buckets) == huge(i)) RETURN ! reached maximum - stay there.
220  new_cap = huge(i) ! grow as far as possible
221  END IF
222  cpassert(new_cap >= 1)
223  cpassert(4*hash_map%size < 3*new_cap)
224 
225  old_size = hash_map%size
226  old_buckets => hash_map%buckets
227  ALLOCATE (hash_map%buckets(new_capacity))
228  hash_map%size = 0
229  DO i = 1, size(old_buckets)
230  item => old_buckets(i)%p
231  DO WHILE (ASSOCIATED(item))
232  CALL callgraph_set_hashed(hash_map, item%key, item%value, item%hash)
233  prev_item => item
234  item => item%next
235  DEALLOCATE (prev_item)
236  END DO
237  END DO
238 
239  DEALLOCATE (old_buckets)
240 
241  cpassert(old_size == hash_map%size)
242  END SUBROUTINE callgraph_change_capacity
243 
244 ! **************************************************************************************************
245 !> \brief Gets a value for a given key from the hash map.
246 !> If the key is not found the default_value will be returned.
247 !> If the key is not found and default_value was not provided the program stops.
248 !> \param hash_map ...
249 !> \param key ...
250 !> \param default_value ...
251 !> \return ...
252 !> \author Ole Schuett
253 ! **************************************************************************************************
254  FUNCTION callgraph_get(hash_map, key, default_value) RESULT(value)
255  TYPE(callgraph_type), INTENT(in) :: hash_map
256  INTEGER(kind=int_4), DIMENSION(2), INTENT(in) :: key
257  TYPE(call_stat_type), POINTER, INTENT(in), OPTIONAL :: default_value
258  TYPE(call_stat_type), POINTER :: value
259  TYPE(private_item_type), POINTER :: item
260  INTEGER(KIND=int_8) :: hash, idx
261 
262  cpassert(ASSOCIATED(hash_map%buckets))
263 
264  hash = callgraph_hash_function(key)
265  idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
266 
267  item => hash_map%buckets(idx)%p
268  DO WHILE (ASSOCIATED(item))
269  IF (item%hash == hash) THEN
270  IF (callgraph_keys_equal(item%key, key)) THEN
271  value =>item%value
272  RETURN
273  END IF
274  END IF
275  item => item%next
276  END DO
277 
278  IF (PRESENT(default_value)) THEN
279  value =>default_value
280  RETURN
281  END IF
282 
283  cpabort("Key not found.")
284  END FUNCTION callgraph_get
285 
286 ! **************************************************************************************************
287 !> \brief Remove the value for a given key from the hash map.
288 !> If the key is not found the program stops.
289 !> \param hash_map ...
290 !> \param key ...
291 !> \author Ole Schuett
292 ! **************************************************************************************************
293  SUBROUTINE callgraph_del(hash_map, key)
294  TYPE(callgraph_type), INTENT(inout) :: hash_map
295  INTEGER(kind=int_4), DIMENSION(2), INTENT(in) :: key
296  TYPE(private_item_type), POINTER :: item, prev_item
297  INTEGER(KIND=int_8) :: hash, idx
298 
299  cpassert(ASSOCIATED(hash_map%buckets))
300 
301  hash = callgraph_hash_function(key)
302  idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
303 
304  item => hash_map%buckets(idx)%p
305  prev_item => null()
306  DO WHILE (ASSOCIATED(item))
307  IF (item%hash == hash) THEN
308  IF (callgraph_keys_equal(item%key, key)) THEN
309  IF (ASSOCIATED(prev_item)) THEN
310  prev_item%next => item%next
311  ELSE
312  hash_map%buckets(idx)%p => item%next
313  END IF
314  DEALLOCATE (item)
315  hash_map%size = hash_map%size - 1
316  RETURN
317  END IF
318  END IF
319  prev_item => item
320  item => item%next
321  END DO
322 
323  cpabort("Key not found.")
324  END SUBROUTINE callgraph_del
325 
326 ! **************************************************************************************************
327 !> \brief Returns the number of key/value-items currently stored in the hash map.
328 !> \param hash_map ...
329 !> \return ...
330 !> \author Ole Schuett
331 ! **************************************************************************************************
332  FUNCTION callgraph_size(hash_map) RESULT(size)
333  TYPE(callgraph_type), INTENT(IN) :: hash_map
334  INTEGER :: size
335 
336  cpassert(ASSOCIATED(hash_map%buckets))
337  size = hash_map%size
338  END FUNCTION callgraph_size
339 
340 ! **************************************************************************************************
341 !> \brief Checks whether a given key is currently stored in the hash_map.
342 !> \param hash_map ...
343 !> \param key ...
344 !> \return ...
345 !> \author Ole Schuett
346 ! **************************************************************************************************
347  FUNCTION callgraph_haskey(hash_map, key) RESULT(res)
348  TYPE(callgraph_type), INTENT(IN) :: hash_map
349  INTEGER(kind=int_4), DIMENSION(2), INTENT(IN) :: key
350  LOGICAL :: res
351  TYPE(private_item_type), POINTER :: item
352  INTEGER(KIND=int_8) :: hash, idx
353 
354  cpassert(ASSOCIATED(hash_map%buckets))
355 
356  res = .false.
357  IF (hash_map%size == 0) RETURN
358 
359  hash = callgraph_hash_function(key)
360  idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
361 
362  item => hash_map%buckets(idx)%p
363  DO WHILE (ASSOCIATED(item))
364  IF (item%hash == hash) THEN
365  IF (callgraph_keys_equal(item%key, key)) THEN
366  res = .true.
367  return
368  END IF
369  END IF
370  item => item%next
371  END DO
372 
373  END FUNCTION callgraph_haskey
374 
375 ! **************************************************************************************************
376 !> \brief Returns a pointer to an array of all key/value-items stored in the hash map.
377 !> Caution: The caller is responsible for deallocating targeted array after usage.
378 !> \param hash_map ...
379 !> \return ...
380 !> \author Ole Schuett
381 ! **************************************************************************************************
382  FUNCTION callgraph_items(hash_map) RESULT(items)
383  TYPE(callgraph_type), INTENT(IN) :: hash_map
384  TYPE(callgraph_item_type), DIMENSION(:), POINTER :: items
385 
386  TYPE(private_item_type), POINTER :: item
387  INTEGER :: i, j
388 
389  cpassert(ASSOCIATED(hash_map%buckets))
390 
391  ALLOCATE (items(hash_map%size))
392  j = 1
393  DO i = 1, size(hash_map%buckets)
394  item => hash_map%buckets(i)%p
395  DO WHILE (ASSOCIATED(item))
396  items(j)%key =item%key
397  items(j)%value =>item%value
398  j = j + 1
399  item => item%next
400  END DO
401  END DO
402 
403  cpassert(j == hash_map%size + 1)
404  END FUNCTION callgraph_items
405 
406 ! **************************************************************************************************
407 !> \brief Copies all key/values-items from one hash map to another.
408 !> Afterwards hash_map will contain all items from the from_hash_map and
409 !> additionally all its previous items, which were not overwritten.
410 !> The two hash maps have to be of the same type.
411 !> \param hash_map destination of items
412 !> \param from_hash_map source of items - will not be change
413 !> \author Ole Schuett
414 ! **************************************************************************************************
415  SUBROUTINE callgraph_update(hash_map, from_hash_map)
416  TYPE(callgraph_type), INTENT(inout) :: hash_map
417  TYPE(callgraph_type), INTENT(in) :: from_hash_map
418  TYPE(callgraph_item_type), DIMENSION(:), POINTER :: from_items
419  INTEGER :: i
420 
421  cpassert(ASSOCIATED(hash_map%buckets))
422  cpassert(ASSOCIATED(from_hash_map%buckets))
423 
424  from_items => callgraph_items(from_hash_map)
425  DO i = 1, size(from_items)
426  CALL callgraph_set(hash_map, from_items(i)%key, from_items(i)%value)
427  END DO
428  DEALLOCATE (from_items)
429  END SUBROUTINE callgraph_update
430 
431 
432 ! **************************************************************************************************
433 !> \brief ...
434 !> \param key ...
435 !> \return ...
436 ! **************************************************************************************************
437  PURE FUNCTION callgraph_hash_function(key) RESULT(hash)
438  INTEGER(kind=int_4), DIMENSION(2), INTENT(in) :: key
439  INTEGER(KIND=int_8) :: hash
440 
441  INTEGER(kind=int_8) :: k1, k2
442  k1 = key(1) ! cast to int_8
443  k2 = key(2)
444  hash = ior(k1, ishft(k2, 32))
445  END FUNCTION callgraph_hash_function
446 
447 ! **************************************************************************************************
448 !> \brief ...
449 !> \param key ...
450 !> \return ...
451 ! **************************************************************************************************
452  PURE FUNCTION callgraph_keys_equal(key1, key2) RESULT(res)
453  INTEGER(kind=int_4), DIMENSION(2), INTENT(in) :: key1, key2
454  LOGICAL :: res
455 
456  res = all(key1 == key2)
457  END FUNCTION callgraph_keys_equal
458 
459 END MODULE callgraph
static unsigned int hash(const dbm_task_t task)
Private hash function based on Szudzik's elegant pairing. Using unsigned int to return a positive num...
static GRID_HOST_DEVICE int idx(const orbital a)
Return coset index of given orbital angular momentum.
Definition: grid_common.h:153
subroutine, public callgraph_destroy(hash_map)
Deallocated the internal data-structures if the given hash map. Caution: If the stored keys or values...
Definition: callgraph.F:120
integer function, public callgraph_size(hash_map)
Returns the number of key/value-items currently stored in the hash map.
Definition: callgraph.F:333
subroutine, public callgraph_init(hash_map, initial_capacity)
Allocates the internal data-structures of the given hash map.
Definition: callgraph.F:78
type(callgraph_item_type) function, dimension(:), pointer, public callgraph_items(hash_map)
Returns a pointer to an array of all key/value-items stored in the hash map. Caution: The caller is r...
Definition: callgraph.F:383
logical function, public callgraph_haskey(hash_map, key)
Checks whether a given key is currently stored in the hash_map.
Definition: callgraph.F:348
type(call_stat_type) function, pointer, public callgraph_get(hash_map, key, default_value)
Gets a value for a given key from the hash map. If the key is not found the default_value will be ret...
Definition: callgraph.F:255
subroutine, public callgraph_set(hash_map, key, value)
Stores, and possibly overwrites, a given value under a given key.
Definition: callgraph.F:147
Defines the basic variable types.
Definition: kinds.F:23
integer, parameter, public int_8
Definition: kinds.F:54
integer, parameter, public int_4
Definition: kinds.F:51
Types used by timings.F and timings_report.F The types in this module are used within dict or list,...