@@ -38,12 +38,77 @@ BOOL canBeRepresentedAsLong(NSNumber *num) {
38
38
return YES ;
39
39
}
40
40
41
+ // Running through CompoundWrites for all update paths has been shown to
42
+ // be a 20% pessimization in microbenchmarks. This is because it slows
43
+ // down by O(N) of the write queue length. To eliminate the performance
44
+ // hit, we wrap around existing data of either snapshot or CompoundWrite
45
+ // (allowing us to share code) and read from the CompoundWrite only when/where
46
+ // we need to calculate an incremented value's prior state.
47
+ @protocol ValueProvider <NSObject >
48
+ - (id <ValueProvider>)getChild : (NSString *)pathSegment ;
49
+ - (id <FNode>)value ;
50
+ @end
51
+
52
+ @interface DeferredValueProvider : NSObject <ValueProvider>
53
+ - (instancetype )initWithSyncTree : (FSyncTree *)tree atPath : (FPath *)path ;
54
+ - (id <ValueProvider>)getChild : (NSString *)pathSegment ;
55
+ - (id <FNode>)value ;
56
+ @property FPath *path;
57
+ @property FSyncTree *tree;
58
+ @end
59
+
60
+ @interface ExistingValueProvider : NSObject <ValueProvider>
61
+ - (instancetype )initWithSnapshot : (id <FNode>)snapshot ;
62
+ - (id <ValueProvider>)getChild : (NSString *)pathSegment ;
63
+ - (id <FNode>)value ;
64
+ @property id <FNode> snapshot;
65
+ @end
66
+
67
+ @implementation DeferredValueProvider
68
+ - (instancetype )initWithSyncTree : (FSyncTree *)tree atPath : (FPath *)path {
69
+ self.tree = tree;
70
+ self.path = path;
71
+ return self;
72
+ }
73
+
74
+ - (id <ValueProvider>)getChild : (NSString *)pathSegment {
75
+ FPath *child = [self .path childFromString: pathSegment];
76
+ return [[DeferredValueProvider alloc ] initWithSyncTree: self .tree
77
+ atPath: child];
78
+ }
79
+
80
+ - (id <FNode>)value {
81
+ return [self .tree calcCompleteEventCacheAtPath: self .path
82
+ excludeWriteIds: @[]];
83
+ }
84
+ @end
85
+
86
+ @implementation ExistingValueProvider
87
+ - (instancetype )initWithSnapshot : (id <FNode>)snapshot {
88
+ self.snapshot = snapshot;
89
+ return self;
90
+ }
91
+
92
+ - (id <ValueProvider>)getChild : (NSString *)pathSegment {
93
+ return [[ExistingValueProvider alloc ]
94
+ initWithSnapshot: [self .snapshot getImmediateChild: pathSegment]];
95
+ }
96
+
97
+ - (id <FNode>)value {
98
+ return self.snapshot ;
99
+ }
100
+ @end
101
+
41
102
@interface FServerValues ()
42
103
+ (id )resolveScalarServerOp : (NSString *)op
43
104
withServerValues : (NSDictionary *)serverValues ;
44
105
+ (id )resolveComplexServerOp : (NSDictionary *)op
45
- withExisting : (id <FNode >)existing
106
+ withExisting : (id <ValueProvider >)existing
46
107
serverValues : (NSDictionary *)serverValues ;
108
+ + (id <FNode>)resolveDeferredValueSnapshot : (id <FNode>)node
109
+ withJITExisting : (id <ValueProvider>)existing
110
+ serverValues : (NSDictionary *)serverValues ;
111
+
47
112
@end
48
113
49
114
@implementation FServerValues
@@ -54,7 +119,7 @@ + (NSDictionary *)generateServerValues:(id<FClock>)clock {
54
119
}
55
120
56
121
+ (id )resolveDeferredValue : (id )val
57
- withExisting : (id <FNode >)existing
122
+ withExisting : (id <ValueProvider >)existing
58
123
serverValues : (NSDictionary *)serverValues {
59
124
if (![val isKindOfClass: [NSDictionary class ]]) {
60
125
return val;
@@ -81,7 +146,7 @@ + (id)resolveScalarServerOp:(NSString *)op
81
146
}
82
147
83
148
+ (id )resolveComplexServerOp : (NSDictionary *)op
84
- withExisting : (id <FNode>) existing
149
+ withExisting : (id <ValueProvider>) jitExisting
85
150
serverValues : (NSDictionary *)serverValues {
86
151
// Only increment is supported as of now
87
152
if (op[kIncrement ] == nil ) {
@@ -90,6 +155,7 @@ + (id)resolveComplexServerOp:(NSDictionary *)op
90
155
91
156
// Incrementing a non-number sets the value to the incremented amount
92
157
NSNumber *delta = op[kIncrement ];
158
+ id <FNode> existing = jitExisting.value ;
93
159
if (![existing isLeafNode ]) {
94
160
return delta;
95
161
}
@@ -117,49 +183,54 @@ + (id)resolveComplexServerOp:(NSDictionary *)op
117
183
}
118
184
119
185
+ (FCompoundWrite *)resolveDeferredValueCompoundWrite : (FCompoundWrite *)write
120
- withExisting : (id <FNode>)existing
186
+ withSyncTree : (FSyncTree *)tree
187
+ atPath : (FPath *)path
121
188
serverValues :
122
189
(NSDictionary *)serverValues {
123
190
__block FCompoundWrite *resolved = write;
124
- [write enumerateWrites: ^(FPath *path, id <FNode> node, BOOL *stop) {
191
+ [write enumerateWrites: ^(FPath *subPath, id <FNode> node, BOOL *stop) {
192
+ id <ValueProvider> existing =
193
+ [[DeferredValueProvider alloc ] initWithSyncTree: tree
194
+ atPath: [path child: subPath]];
125
195
id <FNode> resolvedNode =
126
196
[FServerValues resolveDeferredValueSnapshot: node
127
- withExisting : existing
197
+ withJITExisting : existing
128
198
serverValues: serverValues];
129
199
// Node actually changed, use pointer inequality here
130
200
if (resolvedNode != node) {
131
- resolved = [resolved addWrite: resolvedNode atPath: path ];
201
+ resolved = [resolved addWrite: resolvedNode atPath: subPath ];
132
202
}
133
203
}];
134
204
return resolved;
135
205
}
136
206
137
- + (id )resolveDeferredValueTree : (FSparseSnapshotTree *)tree
138
- withExisting : (id <FNode>)existing
139
- serverValues : (NSDictionary *)serverValues {
140
- FSparseSnapshotTree *resolvedTree = [[FSparseSnapshotTree alloc ] init ];
141
- [tree
142
- forEachTreeAtPath: [FPath empty ]
143
- do: ^(FPath *path, id <FNode> node) {
144
- [resolvedTree
145
- rememberData:
146
- [FServerValues
147
- resolveDeferredValueSnapshot: node
148
- withExisting:
149
- [existing
150
- getChild: path]
151
- serverValues: serverValues]
152
- onPath: path];
153
- }];
154
- return resolvedTree;
207
+ + (id <FNode>)resolveDeferredValueSnapshot : (id <FNode>)node
208
+ withSyncTree : (FSyncTree *)tree
209
+ atPath : (FPath *)path
210
+ serverValues : (NSDictionary *)serverValues {
211
+ id <ValueProvider> jitExisting =
212
+ [[DeferredValueProvider alloc ] initWithSyncTree: tree atPath: path];
213
+ return [FServerValues resolveDeferredValueSnapshot: node
214
+ withJITExisting: jitExisting
215
+ serverValues: serverValues];
155
216
}
156
217
157
218
+ (id <FNode>)resolveDeferredValueSnapshot : (id <FNode>)node
158
219
withExisting : (id <FNode>)existing
159
220
serverValues : (NSDictionary *)serverValues {
221
+ id <ValueProvider> jitExisting =
222
+ [[ExistingValueProvider alloc ] initWithSnapshot: existing];
223
+ return [FServerValues resolveDeferredValueSnapshot: node
224
+ withJITExisting: jitExisting
225
+ serverValues: serverValues];
226
+ }
227
+
228
+ + (id <FNode>)resolveDeferredValueSnapshot : (id <FNode>)node
229
+ withJITExisting : (id <ValueProvider>)existing
230
+ serverValues : (NSDictionary *)serverValues {
160
231
id priorityVal =
161
232
[FServerValues resolveDeferredValue: [[node getPriority ] val ]
162
- withExisting: existing.getPriority
233
+ withExisting: [ existing getChild: @" .priority " ]
163
234
serverValues: serverValues];
164
235
id <FNode> priority = [FSnapshotUtilities nodeFrom: priorityVal];
165
236
@@ -184,7 +255,7 @@ + (id)resolveDeferredValueTree:(FSparseSnapshotTree *)tree
184
255
id <FNode> childNode, BOOL *stop) {
185
256
id newChildNode = [FServerValues
186
257
resolveDeferredValueSnapshot: childNode
187
- withExisting : [existing getImmediateChild : childKey]
258
+ withJITExisting : [existing getChild : childKey]
188
259
serverValues: serverValues];
189
260
if (![newChildNode isEqual: childNode]) {
190
261
newNode = [newNode updateImmediateChild: childKey
0 commit comments