/src/connectedhomeip/src/app/AttributeAccessInterfaceCache.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * |
3 | | * Copyright (c) 2024 Project CHIP Authors |
4 | | * All rights reserved. |
5 | | * |
6 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | | * you may not use this file except in compliance with the License. |
8 | | * You may obtain a copy of the License at |
9 | | * |
10 | | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | | * |
12 | | * Unless required by applicable law or agreed to in writing, software |
13 | | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | | * See the License for the specific language governing permissions and |
16 | | * limitations under the License. |
17 | | */ |
18 | | #pragma once |
19 | | |
20 | | #include <stddef.h> |
21 | | |
22 | | #include <app/AttributeAccessInterface.h> |
23 | | #include <lib/core/DataModelTypes.h> |
24 | | |
25 | | namespace chip { |
26 | | namespace app { |
27 | | |
28 | | /** |
29 | | * @brief Cache to make look-up of AttributeAccessInterface (AAI) instances faster. |
30 | | * |
31 | | * This cache makes use of the fact that looking-up AttributeAccessInterface |
32 | | * instances is usually done in loops, during read/subscription wildcard |
33 | | * expansion, and there is a significant amount of locality. |
34 | | * |
35 | | * This cache records both "used" (i.e. uses AAI) and the single last |
36 | | * "unused" (i.e. does NOT use AAI) entries. Combining positive/negative |
37 | | * lookup led to factor of ~10 reduction of AAI lookups in total for wildcard |
38 | | * reads on chip-all-clusters-app, with a cache size of 1. Increasing the size did not |
39 | | * significantly improve the performance. |
40 | | */ |
41 | | class AttributeAccessInterfaceCache |
42 | | { |
43 | | public: |
44 | | enum class CacheResult |
45 | | { |
46 | | kCacheMiss, |
47 | | kDefinitelyUnused, |
48 | | kDefinitelyUsed |
49 | | }; |
50 | | |
51 | 1 | AttributeAccessInterfaceCache() { Invalidate(); } |
52 | | |
53 | | /** |
54 | | * @brief Invalidate the whole cache. Must be called every time list of AAI registrations changes. |
55 | | */ |
56 | | void Invalidate() |
57 | 90 | { |
58 | 90 | for (auto & entry : mCacheSlots) |
59 | 90 | { |
60 | 90 | entry.Invalidate(); |
61 | 90 | } |
62 | 90 | mLastUnusedEntry.Invalidate(); |
63 | 90 | } |
64 | | |
65 | | /** |
66 | | * @brief Mark that we know a given <`endpointId`, `clusterId`> uses AAI, with instance `attrInterface` |
67 | | */ |
68 | | void MarkUsed(EndpointId endpointId, ClusterId clusterId, AttributeAccessInterface * attrInterface) |
69 | 0 | { |
70 | 0 | GetCacheSlot(endpointId, clusterId)->Set(endpointId, clusterId, attrInterface); |
71 | 0 | } |
72 | | |
73 | | /** |
74 | | * @brief Mark that we know a given <`endpointId`, `clusterId`> does NOT use AAI. |
75 | | */ |
76 | 0 | void MarkUnused(EndpointId endpointId, ClusterId clusterId) { mLastUnusedEntry.Set(endpointId, clusterId, nullptr); } |
77 | | |
78 | | /** |
79 | | * @brief Get the AttributeAccessInterface instance for a given <`endpointId`, `clusterId`>, if present in cache. |
80 | | * |
81 | | * @param endpointId - Endpoint ID to look-up. |
82 | | * @param clusterId - Cluster ID to look-up. |
83 | | * @param outAttributeAccess - If not null, and Get returns `kDefinitelyUsed`, then this is set to the instance pointer. |
84 | | * @return a for whether the entry is actually used or not. |
85 | | */ |
86 | | CacheResult Get(EndpointId endpointId, ClusterId clusterId, AttributeAccessInterface ** outAttributeAccess) |
87 | 0 | { |
88 | 0 | if (mLastUnusedEntry.Matches(endpointId, clusterId)) |
89 | 0 | { |
90 | 0 | return CacheResult::kDefinitelyUnused; |
91 | 0 | } |
92 | | |
93 | 0 | AttributeAccessCacheEntry * cacheSlot = GetCacheSlot(endpointId, clusterId); |
94 | 0 | if (cacheSlot->Matches(endpointId, clusterId) && (cacheSlot->accessor != nullptr)) |
95 | 0 | { |
96 | 0 | if (outAttributeAccess != nullptr) |
97 | 0 | { |
98 | 0 | *outAttributeAccess = cacheSlot->accessor; |
99 | 0 | } |
100 | 0 | return CacheResult::kDefinitelyUsed; |
101 | 0 | } |
102 | | |
103 | 0 | return CacheResult::kCacheMiss; |
104 | 0 | } |
105 | | |
106 | | private: |
107 | | struct AttributeAccessCacheEntry |
108 | | { |
109 | | EndpointId endpointId = kInvalidEndpointId; |
110 | | ClusterId clusterId = kInvalidClusterId; |
111 | | AttributeAccessInterface * accessor = nullptr; |
112 | | |
113 | | void Invalidate() |
114 | 180 | { |
115 | 180 | endpointId = kInvalidEndpointId; |
116 | 180 | clusterId = kInvalidClusterId; |
117 | 180 | accessor = nullptr; |
118 | 180 | } |
119 | | |
120 | | void Set(EndpointId theEndpointId, ClusterId theClusterId, AttributeAccessInterface * theAccessor) |
121 | 0 | { |
122 | 0 | endpointId = theEndpointId; |
123 | 0 | clusterId = theClusterId; |
124 | 0 | accessor = theAccessor; |
125 | 0 | } |
126 | | |
127 | | bool Matches(EndpointId theEndpointId, ClusterId theClusterId) const |
128 | 0 | { |
129 | 0 | return (endpointId == theEndpointId) && (clusterId == theClusterId); |
130 | 0 | } |
131 | | }; |
132 | | |
133 | | AttributeAccessCacheEntry * GetCacheSlot(EndpointId endpointId, ClusterId clusterId) |
134 | 0 | { |
135 | 0 | (void) endpointId; |
136 | 0 | (void) clusterId; |
137 | 0 | return &mCacheSlots[0]; |
138 | 0 | } |
139 | | |
140 | | AttributeAccessCacheEntry mCacheSlots[1]; |
141 | | AttributeAccessCacheEntry mLastUnusedEntry; |
142 | | }; |
143 | | |
144 | | } // namespace app |
145 | | } // namespace chip |