1 | /*
|
2 | * Souffle - A Datalog Compiler
|
3 | * Copyright (c) 2021, The Souffle Developers. All rights reserved
|
4 | * Licensed under the Universal Permissive License v 1.0 as shown at:
|
5 | * - https://opensource.org/licenses/UPL
|
6 | * - <souffle root>/licenses/SOUFFLE-UPL.txt
|
7 | */
|
8 | #pragma once
|
9 |
|
10 | #include "ConcurrentInsertOnlyHashMap.h"
|
11 | #include "souffle/utility/ParallelUtil.h"
|
12 |
|
13 | namespace souffle {
|
14 | /**
|
15 | * A simple implementation of a cache that can be used during interation and compilation
|
16 | * to store frequently used values or values that are expensive to compute (e.g., regex).
|
17 | */
|
18 | template <class Key, class Value, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>,
|
19 | class KeyFactory = details::Factory<Key>>
|
20 | struct ConcurrentCache {
|
21 | #ifdef _OPENMP
|
22 | using CacheImpl = ConcurrentInsertOnlyHashMap<ConcurrentLanes, Key, Value, Hash, KeyEqual, KeyFactory>;
|
23 | #else
|
24 | using CacheImpl = ConcurrentInsertOnlyHashMap<SeqConcurrentLanes, Key, Value, Hash, KeyEqual, KeyFactory>;
|
25 | #endif
|
26 |
|
27 | ConcurrentCache(const std::size_t LaneCount = 1) : lanes(LaneCount), cache(LaneCount, 8) {}
|
28 | ~ConcurrentCache() = default;
|
29 |
|
30 | void setNumLanes(const std::size_t NumLanes) {
|
31 | lanes.setNumLanes(NumLanes);
|
32 | cache.setNumLanes(NumLanes);
|
33 | }
|
34 |
|
35 | /**
|
36 | * @brief Lookup the specified key in the cache.
|
37 | *
|
38 | * If the key is in the cache, then the associated value is returned. Otherwise,
|
39 | * if the key, is not in the cache, the supplied function is invoked to construct
|
40 | * the value from the key.
|
41 | *
|
42 | * The cache is not modified if the constructor function throws an exception.
|
43 | * @param key the key for caching
|
44 | * @param constructor a functor that takes a key and produces a value
|
45 | * @return the cached value that is associated with the key
|
46 | */
|
47 | template <class CTOR>
|
48 | const Value& getOrCreate(const Key& key, const CTOR& constructor) {
|
49 | typename CacheImpl::lane_id lane = lanes.threadLane();
|
50 | auto entry = cache.weakFind(lane, key);
|
51 | if (entry == nullptr) {
|
52 | auto node = cache.node(constructor(key));
|
53 | auto result = cache.get(lane, node, key);
|
54 | if (!result.second) {
|
55 | // we need to delete the temporary node, since someone else
|
56 | // already created the same node concurrently
|
57 | delete node;
|
58 | }
|
59 | entry = result.first;
|
60 | }
|
61 | return entry->second;
|
62 | }
|
63 |
|
64 | /**
|
65 | * @brief Lookup the value associated with the specified key.
|
66 | *
|
67 | * If the key does not exist, constructs a value using
|
68 | * the key.
|
69 | *
|
70 | * This function forwards to getOrCreate
|
71 | * @param key the key to lookup
|
72 | * @return the cached value that is associated with the key
|
73 | */
|
74 | inline const Value& getOrCreate(const Key& key) {
|
75 | return getOrCreate(key, [](auto p) { return Value(p); });
|
76 | }
|
77 |
|
78 | ConcurrentLanes lanes;
|
79 |
|
80 | CacheImpl cache;
|
81 | };
|
82 |
|
83 | } // namespace souffle
|