@@ -106,35 +106,12 @@ macro_rules! impl_query_archetype {
106
106
format!( "cached={cached} arch={} pov={} comp={}" , A :: name( ) , $N, $M)
107
107
) ;
108
108
109
- let mut latest_at_callback = |query: & LatestAtQuery , cache: & mut crate :: LatestAtCache | {
110
- re_tracing:: profile_scope!( "latest_at" , format!( "{query:?}" ) ) ;
111
-
112
- let bucket = cache. entry( query. at) . or_default( ) ;
113
- // NOTE: Implicitly dropping the write guard here: the LatestAtCache is free once again!
114
-
115
- if bucket. is_empty( ) {
116
- re_tracing:: profile_scope!( "fill" ) ;
117
-
118
- let now = web_time:: Instant :: now( ) ;
119
- // TODO(cmc): cache deduplication.
120
- let arch_view = query_archetype:: <A >( store, & query, entity_path) ?;
121
-
122
- bucket. [ <insert_pov $N _comp$M>] :: <A , $( $pov, ) + $( $comp, ) * >( query. at, & arch_view) ?;
123
-
124
- let elapsed = now. elapsed( ) ;
125
- :: re_log:: trace!(
126
- store_id=%store. id( ) ,
127
- %entity_path,
128
- archetype=%A :: name( ) ,
129
- "cached new entry in {elapsed:?} ({:0.3} entries/s)" ,
130
- 1f64 / elapsed. as_secs_f64( )
131
- ) ;
132
- }
133
109
110
+ let mut iter_results = |bucket: & crate :: CacheBucket | -> crate :: Result <( ) > {
134
111
re_tracing:: profile_scope!( "iter" ) ;
135
112
136
113
let it = itertools:: izip!(
137
- bucket. iter_pov_times ( ) ,
114
+ bucket. iter_pov_data_times ( ) ,
138
115
bucket. iter_pov_instance_keys( ) ,
139
116
$( bucket. iter_component:: <$pov>( )
140
117
. ok_or_else( || re_query:: ComponentNotFoundError ( <$pov>:: name( ) ) ) ?, ) +
@@ -156,6 +133,71 @@ macro_rules! impl_query_archetype {
156
133
Ok ( ( ) )
157
134
} ;
158
135
136
+ let mut latest_at_callback = |query: & LatestAtQuery , latest_at_cache: & mut crate :: LatestAtCache | {
137
+ re_tracing:: profile_scope!( "latest_at" , format!( "{query:?}" ) ) ;
138
+
139
+ let crate :: LatestAtCache { per_query_time, per_data_time } = latest_at_cache;
140
+
141
+ let query_time_bucket_at_query_time = match per_query_time. entry( query. at) {
142
+ std:: collections:: btree_map:: Entry :: Occupied ( query_time_bucket_at_query_time) => {
143
+ // Fastest path: we have an entry for this exact query time, no need to look any
144
+ // further.
145
+ return iter_results( & query_time_bucket_at_query_time. get( ) . read( ) ) ;
146
+ }
147
+ entry @ std:: collections:: btree_map:: Entry :: Vacant ( _) => entry,
148
+ } ;
149
+
150
+ let arch_view = query_archetype:: <A >( store, & query, entity_path) ?;
151
+ // TODO(cmc): actual timeless caching support.
152
+ let data_time = arch_view. data_time( ) . unwrap_or( TimeInt :: MIN ) ;
153
+
154
+ // Fast path: we've run the query and realized that we already have the data for the resulting
155
+ // _data_ time, so let's use that to avoid join & deserialization costs.
156
+ if let Some ( data_time_bucket_at_data_time) = per_data_time. get( & data_time) {
157
+ * query_time_bucket_at_query_time. or_default( ) = std:: sync:: Arc :: clone( & data_time_bucket_at_data_time) ;
158
+
159
+ // We now know for a fact that a query at that data time would yield the same
160
+ // results: copy the bucket accordingly so that the next cache hit for that query
161
+ // time ends up taking the fastest path.
162
+ let query_time_bucket_at_data_time = per_query_time. entry( data_time) ;
163
+ * query_time_bucket_at_data_time. or_default( ) = std:: sync:: Arc :: clone( & data_time_bucket_at_data_time) ;
164
+
165
+ return iter_results( & data_time_bucket_at_data_time. read( ) ) ;
166
+ }
167
+
168
+ let query_time_bucket_at_query_time = query_time_bucket_at_query_time. or_default( ) ;
169
+
170
+ // Slowest path: this is a complete cache miss.
171
+ {
172
+ re_tracing:: profile_scope!( "fill" ) ;
173
+
174
+ // Grabbing the current time is quite costly on web.
175
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
176
+ let now = web_time:: Instant :: now( ) ;
177
+
178
+ let mut query_time_bucket_at_query_time = query_time_bucket_at_query_time. write( ) ;
179
+ query_time_bucket_at_query_time. [ <insert_pov$N _comp$M>] :: <A , $( $pov, ) + $( $comp, ) * >( query. at, & arch_view) ?;
180
+
181
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
182
+ {
183
+ let elapsed = now. elapsed( ) ;
184
+ :: re_log:: trace!(
185
+ store_id=%store. id( ) ,
186
+ %entity_path,
187
+ archetype=%A :: name( ) ,
188
+ "cached new entry in {elapsed:?} ({:0.3} entries/s)" ,
189
+ 1f64 / elapsed. as_secs_f64( )
190
+ ) ;
191
+ }
192
+ }
193
+
194
+ let data_time_bucket_at_data_time = per_data_time. entry( data_time) ;
195
+ * data_time_bucket_at_data_time. or_default( ) = std:: sync:: Arc :: clone( & query_time_bucket_at_query_time) ;
196
+
197
+ iter_results( & query_time_bucket_at_query_time. read( ) )
198
+ } ;
199
+
200
+
159
201
match & query {
160
202
// TODO(cmc): cached range support
161
203
AnyQuery :: Range ( query) => {
@@ -203,7 +245,7 @@ macro_rules! impl_query_archetype {
203
245
store. id( ) . clone( ) ,
204
246
entity_path. clone( ) ,
205
247
query,
206
- |cache | latest_at_callback( query, cache ) ,
248
+ |latest_at_cache | latest_at_callback( query, latest_at_cache ) ,
207
249
)
208
250
} ,
209
251
}
0 commit comments