Skip to Content
New release 12 available 🎉

Performance & Tuning

Control flow obfuscation trades a little runtime for protection. The lightweight algorithms are essentially free; the flattening algorithms add a dispatcher that runs on every call and therefore cost more. This page compares the algorithms, explains the iterations setting, and shows how to keep hot methods fast.

Comparing the Algorithms

AlgorithmFamilyRelative runtime costVerifiable ILEdition
ifLightweightNegligibleYesAll
gotoLightweightNegligibleYesAll
switchFlatteningHighYesAll
caseFlattening (with switch)HighYesAll
callFlattening (with switch)HighYesAll
valueFlattening (with switch)HighYesAll
chain (Chained State)FlatteningHigh (≈ switch)YesUltimate
token—LowNoAll
underflow—LowNoAll

Reading the table.

  • if and goto add branches and conditions but no dispatcher, so their runtime cost is negligible — you can apply them broadly.
  • The flattening family (switch, case, call, value) and chain rebuild a method around a dispatcher. On a small method called in a tight loop tens of millions of times, a single flattening pass adds roughly an order of magnitude to that method’s per-call time. Chained State stays within a few percent of switch despite being much harder to deobfuscate — the extra protection is essentially free relative to flattening itself.
  • token and underflow are cheap at runtime but produce non-verifiable IL, so Babel disables them on .NET and .NET Core.

These are worst-case figures from a micro-benchmark that does nothing but call one tiny flattened method in a loop. Most methods are not invoked in such hot loops, so the real-world impact of flattening is usually small — but it is still worth reserving flattening for the methods that matter and keeping it off hot paths.

Control Flow Iterations

The obfuscation impact of any control flow algorithm is primarily governed by the number of iterations applied to a method: the higher the count, the more complex the result — and, for the flattening algorithms, the higher the runtime cost, because each pass adds another layer of dispatch.

Babel lets you set the iteration count with --iterations <n> (or ILIterations from MSBuild). Setting it to 0 disables control flow obfuscation. There is an upper limit beyond which a given method’s flow can no longer be altered — it depends on the method’s structure — so increasing the count does not always add more instructions.

Limiting Control Flow Obfuscation

Because flattening adds branches to every method it touches, applying it indiscriminately can slow down performance-critical code. Use XML rules to limit control flow obfuscation to the methods that need it and exclude the rest — for example, compiler-generated methods, which rarely contain intellectual property worth protecting:

<Rules> <Rule name="rule" feature="control flow" exclude="true" applyToMembers="true"> <Target>Classes</Target> <Pattern>*</Pattern> <HasAttribute onEnclosingType="false">System.Runtime.CompilerServices.CompilerGeneratedAttribute</HasAttribute> </Rule> </Rules>

This rule targets the control flow feature with exclude="true", and applyToMembers="true" extends it to all members of the matching classes. The <Target> element selects the kind of element (here, Classes), <Pattern> matches their names (* = all), and <HasAttribute> restricts the match to classes carrying CompilerGeneratedAttribute.

The same approach lets you exclude any method you know to be performance-critical, so you can balance protection against speed: flatten the sensitive code, and leave the hot paths untouched.

Last updated on