Overview
Multi-Class Randomization reduces the size of a constrained-random generation problem by splitting one large opcode class into multiple smaller classes. In the cited instruction-generator architecture, opcodes were divided into categories that matched the knobs or weights exposed by the test interface. [C1]
The technique uses a base instruction class for data members and methods common to all instruction types, while each opcode-category child class contains only the constraints specific to that category. [C2]
Motivation
The technique was introduced as an alternative to two less satisfactory approaches for x86 opcode generation. Serial randomization met speed and memory goals but produced skewed distributions because each opcode portion was generated sequentially, leaving no control over the overall distribution. A simple constrained-random approach fixed the distribution issue, but the complex x86 instruction set made the randomization problem too slow and memory-intensive. [C3]
Architecture
A Multi-Class Randomization architecture typically separates instruction generation into:
- a base instruction class containing common data members, common constraints, and common methods such as setting, printing, and packing data; and
- multiple opcode-category child classes containing constraints specific to each opcode set. [C2]
Within each child class, the cited implementation retained a structure similar to the original single-class code, using implication operators based on opcode type. [C4]
Generation flow
In the cited design, the upper-layer random sequence was controlled only by knobs or switches. It first chose the opcode category, then allocated the appropriate child-class object and added it into the sequence. This avoided direct test-layer constraints on child-class internals. [C5]
If the test layer directly controls lower-level subclass items, the cited source recommends making the subclass-selection decisions first. In that situation, a wrapper class may be needed to constrain all test-controlled variables, randomize those variables first, and then allocate and randomize the correct subclass object in a second generation phase. [C6]
Performance characteristics
The reported performance improvement came from reducing the number of variables and constraints seen by the solver. Compared with the original single-class implementation, the multi-class implementation had seven times fewer constraints, allowing the solver to calculate solutions more efficiently. [C7]
Runtime improved for both solvers evaluated in the case study. The default RACE solver showed a 4x speedup, while the BDD solver showed a 2x speedup for the measured opcode randomization workloads. [C8]
Memory use was also significantly better for the multi-class architecture. The cited study measured memory for the BDD solver because RACE memory consumption was typically smaller and not a limiting factor. [C9]
Solver and profiling considerations
The VCS constraint profiler was used to analyze runtime and memory behavior. It reported runtime data by cumulative randomize calls, individual randomize calls, and solver partitions. [C10]
For BDD solving, memory can be especially important because the solver elaborates the entire solution space before selecting a solution. That solution space can require substantial memory and time to elaborate, although it may be cached to speed later randomize calls. [C11]
Benefits
The main benefit of Multi-Class Randomization is that the solver only sees the constraints relevant to the selected opcode category. In the cited case study, this preserved distribution control and test-level control while improving speed and memory compared with the single-class constrained-random implementation. [C12]