Skip to content

Commit 4dfd05d

Browse files
committed
feat: Optimize sort by Below
The `Below` function is in the hot path. The slice (x[1..]) operation is not optimized in Dafny. This optimizes this function by turning the recursive slice into a loop over an index into the seq. Further, a bounded integer version is also included.
1 parent b9333fb commit 4dfd05d

File tree

1 file changed

+79
-1
lines changed

1 file changed

+79
-1
lines changed

DynamoDbEncryption/dafny/StructuredEncryption/src/SortCanon.dfy

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,89 @@ module SortCanon {
148148
}
149149
}
150150

151-
predicate method Below(x: seq<uint8>, y: seq<uint8>) {
151+
predicate Below(x: seq<uint8>, y: seq<uint8>) {
152152
|x| != 0 ==>
153153
&& |y| != 0
154154
&& x[0] <= y[0]
155155
&& (x[0] == y[0] ==> Below(x[1..], y[1..]))
156+
} by method {
157+
158+
// The slice x[1..], y[1..] are un-optimized operations in Dafny.
159+
// This means that their usage will result in a lot of data copying.
160+
// Additional, it is very likely that these size of these sequences
161+
// will be less than uint64.
162+
// So writing an optimized version that only works on bounded types
163+
// should further optimized this hot code.
164+
165+
if HasUint64Len(x) && HasUint64Len(y) {
166+
return BoundedBelow(x,y);
167+
}
168+
169+
if |x| == 0 {
170+
assert Below(x, y);
171+
return true;
172+
}
173+
174+
if |y| == 0 {
175+
assert !Below(x, y);
176+
return false;
177+
}
178+
179+
for i := 0 to |x|
180+
invariant i <= |y|
181+
// The function on the initial arguments
182+
// is equal to function applied to the intermediate arguments.
183+
invariant Below(x, y) == Below(x[i..], y[i..])
184+
{
185+
if |y| <= i {
186+
return false;
187+
} else if y[i] < x[i] {
188+
return false;
189+
} else if x[i] < y[i] {
190+
return true;
191+
} else {
192+
assert x[i] == y[i];
193+
}
194+
}
195+
196+
return true;
197+
}
198+
199+
predicate BoundedBelow(x: seq64<uint8>, y: seq64<uint8>)
200+
{
201+
Below(x,y)
202+
} by method {
203+
var xLength := |x| as uint64;
204+
var yLength := |y| as uint64;
205+
206+
if xLength == 0 {
207+
assert BoundedBelow(x, y);
208+
return true;
209+
}
210+
211+
if yLength == 0 {
212+
assert !BoundedBelow(x, y);
213+
return false;
214+
}
215+
216+
for i := 0 to xLength
217+
invariant i <= yLength
218+
// The function on the initial arguments
219+
// is equal to function applied to the intermediate arguments.
220+
invariant BoundedBelow(x, y) == BoundedBelow(x[i..], y[i..])
221+
{
222+
if yLength <= i {
223+
return false;
224+
} else if y[i] < x[i] {
225+
return false;
226+
} else if x[i] < y[i] {
227+
return true;
228+
} else {
229+
assert x[i] == y[i];
230+
}
231+
}
232+
233+
return true;
156234
}
157235

158236
lemma BelowIsTotal()

0 commit comments

Comments
 (0)