CAPÍTULO 2. DESARROLLO DE LA APLICACIÓN
2.3 FASE DE ITERACIONES
2.3.5 TERCERA ENTREGA
2.3.5.6 Pruebas de aceptación tercera iteración
Every deterministic application has a checker since it suffices to duplicate the com- putation to check it. Therefore, a checker’s overhead has an upper bound of 100%. Sometimes we can design a checker that is cheaper asymptotically, but it could turn out to be more expensive than the computation in practice because of constant fac- tors. In those cases, we can choose to use duplication instead of the LWC, so the upper bound for checker overhead is still 100%. This leads to the conceptual LWC-space
Application Complexity 0%
100% LWC cost
Figure 5.4: Conceptual view of LWC existence and complexity
curve shown in Fig. 5.4. We argue below that all applications have LWCs, except at the two ends of that curve: the “very simple” and “very complex” applications, to be defined below.
We have already seen some applications that do not possess LWCs, such as addi- tion and matrix transposition2. What do these applications have in common? Infor-
mally, we notice that they are “very simple”. This is also the case of integer addition, subtraction, and basic boolean operations such as logical and, or, xor, not, nand, nor. In general, the more complex the task, the more structured redundancy there will be in the data usage, the more likely it is that the operation will have an LWC.
What does it mean for a task to be “complex”? Note that each of the applications mentioned above that does not have an LWC uses each bit of data only once. When matrix transposition reads a bit, it knows exactly where it needs to move it, it does not access it again. The same is true for addition, subtraction, and basic boolean operations such as xor, where we look at each bit only once. This leaves no room for redundancy to be created. For an LWC to have good coverage, it also needs to look at every bit, making it as costly as the original computation. In contrast, applications that have LWCs look at the input bits multiple times and combine them in a way that creates redundancy that the LWC can then check without accessing each of the original bits of information as many times.
Remember the addition example from Sec. 5.4; we could not find a check that is lightweight and has good coverage at the same time. For the check to have good coverage, we had to look at each bit of data once; but this is also what the compute was doing, so we could not come out ahead. For computing the sort, each of the N data points to be sorted was compared log(N) times, whereas the LWC only had to look at each input and output data point once. For an h×h window filter, the compute had to access each pixel ≈ h2 times, whereas the LWC had to access each
pixel only once. For the N2 matrix multiplication, the compute had to access each
data point N times, whereas the LWC only had to access each data point once. In general, if we access each data point more times, we create more redundancy that can be exploited by a potential LWC. This rule of thumb is eventually broken as an application becomes even more “complex”, and we lose the lightweight checking ability. This corresponds to the turnaround point in Fig. 5.4. It happens when too much redundancy is stored in a finite amount of space, causing the information to combine and get lost.
Reaching the far end of the high-complexity designs in Fig. 5.4 requires careful design. Popular block ciphers such as AES [1] or cryptographic hash functions such as MD5 [84] are examples of it. These encryption schemes are specifically designed to require a full recomputation to check the answer. They often consist of a series of simple operations, and they are repeated many times over the same input data, so they are not “low complexity”. For example, MD5 consists of four rounds of 16 operations that each involve boolean operations, a non-linear mapping, additions, and permutations. Each bit of the input ends up being read four times, and mixed in with the other data in a structure that achieves an “avalanche effect” [38]. This means that we expect a small change in the input to have a drastic effect at the output. Typically, one bit flip at the input should result in half the bits being flipped at the output. The design intent is that small changes are quickly propagated through the different iterations, leading to each output bit being dependent on each input bit. In other
words, the data is “scrambled” as well as possible, and the output does not provide any information about the input, unless we recompute the whole MD5. Therefore, when we are given the output of the computation, we do not get any “tricks” to check whether it is correct based on the input.