Line data Source code
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 9 : AttributeAccessInterfaceCache() { Invalidate(); }
52 :
53 : /**
54 : * @brief Invalidate the whole cache. Must be called every time list of AAI registrations changes.
55 : */
56 55 : void Invalidate()
57 : {
58 110 : for (auto & entry : mCacheSlots)
59 : {
60 55 : entry.Invalidate();
61 : }
62 55 : mLastUnusedEntry.Invalidate();
63 55 : }
64 :
65 : /**
66 : * @brief Mark that we know a given <`endpointId`, `clusterId`> uses AAI, with instance `attrInterface`
67 : */
68 25 : void MarkUsed(EndpointId endpointId, ClusterId clusterId, AttributeAccessInterface * attrInterface)
69 : {
70 25 : GetCacheSlot(endpointId, clusterId)->Set(endpointId, clusterId, attrInterface);
71 25 : }
72 :
73 : /**
74 : * @brief Mark that we know a given <`endpointId`, `clusterId`> does NOT use AAI.
75 : */
76 5 : 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 5198 : CacheResult Get(EndpointId endpointId, ClusterId clusterId, AttributeAccessInterface ** outAttributeAccess)
87 : {
88 5198 : if (mLastUnusedEntry.Matches(endpointId, clusterId))
89 : {
90 179 : return CacheResult::kDefinitelyUnused;
91 : }
92 :
93 5019 : AttributeAccessCacheEntry * cacheSlot = GetCacheSlot(endpointId, clusterId);
94 5019 : if (cacheSlot->Matches(endpointId, clusterId) && (cacheSlot->accessor != nullptr))
95 : {
96 4989 : if (outAttributeAccess != nullptr)
97 : {
98 4989 : *outAttributeAccess = cacheSlot->accessor;
99 : }
100 4989 : return CacheResult::kDefinitelyUsed;
101 : }
102 :
103 30 : return CacheResult::kCacheMiss;
104 : }
105 :
106 : private:
107 : struct AttributeAccessCacheEntry
108 : {
109 : EndpointId endpointId = kInvalidEndpointId;
110 : ClusterId clusterId = kInvalidClusterId;
111 : AttributeAccessInterface * accessor = nullptr;
112 :
113 110 : void Invalidate()
114 : {
115 110 : endpointId = kInvalidEndpointId;
116 110 : clusterId = kInvalidClusterId;
117 110 : accessor = nullptr;
118 110 : }
119 :
120 30 : void Set(EndpointId theEndpointId, ClusterId theClusterId, AttributeAccessInterface * theAccessor)
121 : {
122 30 : endpointId = theEndpointId;
123 30 : clusterId = theClusterId;
124 30 : accessor = theAccessor;
125 30 : }
126 :
127 10217 : bool Matches(EndpointId theEndpointId, ClusterId theClusterId) const
128 : {
129 10217 : return (endpointId == theEndpointId) && (clusterId == theClusterId);
130 : }
131 : };
132 :
133 5044 : AttributeAccessCacheEntry * GetCacheSlot(EndpointId endpointId, ClusterId clusterId)
134 : {
135 : (void) endpointId;
136 : (void) clusterId;
137 5044 : return &mCacheSlots[0];
138 : }
139 :
140 : AttributeAccessCacheEntry mCacheSlots[1];
141 : AttributeAccessCacheEntry mLastUnusedEntry;
142 : };
143 :
144 : } // namespace app
145 : } // namespace chip
|