Efficient Fail-Fast Dynamic Subtype Checking Rohan Padhye and Koushik - - PowerPoint PPT Presentation

efficient fail fast dynamic subtype checking
SMART_READER_LITE
LIVE PREVIEW

Efficient Fail-Fast Dynamic Subtype Checking Rohan Padhye and Koushik - - PowerPoint PPT Presentation

Efficient Fail-Fast Dynamic Subtype Checking Rohan Padhye and Koushik Sen UC Berkeley VMIL 2019 Dynamic Subtype Checking S S obj = .. T if (obj instance of T) { . ? } obj 2 Dynamic Subtype Checking S S obj = new X() T if (obj


slide-1
SLIDE 1

Efficient Fail-Fast Dynamic Subtype Checking

Rohan Padhye and Koushik Sen UC Berkeley VMIL 2019

slide-2
SLIDE 2

Dynamic Subtype Checking

2

S obj = ….. if (obj instance of T) { …. }

  • bj

S T

?

slide-3
SLIDE 3

Dynamic Subtype Checking

3

S obj = new X() if (obj instance of T) { …. }

S

  • bj

X T

?

X <: T ?

slide-4
SLIDE 4

General Solution: Linear Search

4

S X A B T P

X <: T ?

slide-5
SLIDE 5

General Solution: Linear Search

5

S X A B T P

X <: T ?

slide-6
SLIDE 6

General Solution: Linear Search

6

S X A B T P

X <: T ?

slide-7
SLIDE 7

General Solution: Linear Search

7

S X A B T P

X <: T ?

slide-8
SLIDE 8

General Solution: Linear Search

8

S X A B T P

Subtype test successful X <: T

slide-9
SLIDE 9

General Solution: Linear Search

9

S X A B T P

X <: T ?

slide-10
SLIDE 10

General Solution: Linear Search

10

S X A B T P

X <: T ?

slide-11
SLIDE 11

General Solution: Linear Search

11

S X A B T P

X <: T ?

slide-12
SLIDE 12

General Solution: Linear Search

12

S X A B T P

Subtype test fails X <: T

slide-13
SLIDE 13

Implementations must consider trade-offs

Constant time? Constant space? (per-class) Supports multiple inheritance? Supports open hierarchies?

13

Pick up to 3

slide-14
SLIDE 14

Existing Schemes

14

slide-15
SLIDE 15

Existing Schemes

15

slide-16
SLIDE 16

Case Study: HotSpot JVM

class A implements I1 { … } class B extends A implements I2 { … } interface I5 extends I6, I7, I2 { … } class C extends B implements I3, I4, I5 { … }

16

Primary super I1 I2 I6 I7 I5 I3 I4 Secondary Metadata for C depth super

  • bject

1 A 2 B 3 C 4 5 6 7

I3 C I4 I5 I6 I7 I1 I2 A B

  • bject
slide-17
SLIDE 17

Case Study: HotSpot JVM

class A implements I1 { … } class B extends A implements I2 { … } interface I5 extends I6, I7, I2 { … } class C extends B implements I3, I4, I5 { … } class D extends C class E extends D class F extends E class G extends F class H extends G

17

depth super

  • bject

1 A 2 B 3 C 4 D 5 E 6 F 7 G super I1 I2 I6 I7 I5 I3 I4 H Primary Secondary Metadata for H

slide-18
SLIDE 18

Case Study: HotSpot JVM

18

depth super

  • bject

1 A 2 B 3 C 4 D 5 E 6 F 7 G super I1 I2 I6 I7 I5 I3 I4 H Primary Secondary Metadata for H Primary super I1 I2 I6 I7 I5 I3 I4 Secondary Metadata for C depth super

  • bject

1 A 2 B 3 C 4 5 6 7 X <: C ? X <: D ? X <: H ? X <: I5 ? X.primary[3] == C? X.primary[4] == D? X.secondary_check(H) X.secondary_check(I5)

slide-19
SLIDE 19

Case Study: HotSpot JVM

19

X <: H ? X <: I5 ? X.secondary_check(H) X.secondary_check(I5) X.secondary_check(T) := { if (X.cache == T) return true; if (X == T) return true; foreach S in X.secondaries { if (S == T) { X.cache = S return true; } } return false; } super I1 I2 I6 I7 I5 I3 I4 H Secondary Metadata for H

slide-20
SLIDE 20

Case Study: HotSpot JVM

20

X.secondary_check(T) := { if (X.cache == T) return true; if (X == T) return true; foreach S in X.secondaries { if (S == T) { X.cache = S return true; } } return false; }

Observations:

  • 1. Fast path for success
  • 2. Failure == linear search
slide-21
SLIDE 21

Is this assumption always true?

Are there workloads where dynamic subtype tests often fail?

21

slide-22
SLIDE 22

Case Study: Scala’s Pattern Matching

  • bj match {

case x:A => x.method_on_A() case y:B => y.method_on_B() case z:C => z.method_on_C() … } if (obj instanceof A) { A x = (A) obj; x.method_on_A(); } else if (obj instanceof B) { B y = (B) obj; y.method_on_B(); } else if (obj instanceof C) { C z = (C) obj; z.method_on_C(); } …

22

Compile to JVM

slide-23
SLIDE 23

Profiling Scala’s Pattern Matching

Small workload: scalac Hello.scala

47,597 instanceof tests

93% failed

Large workload: sbt compile # builds scalac

3.1 billion instanceof tests

76% failed 45 million secondary scans

23

slide-24
SLIDE 24

Cast Study: LLVM Compiler Infrastructure

static bool isLoopInvariant(const Value *V, const Loop *L) { if (isa<Constant>(V) || isa<Argument>(V) || isa<GlobalValue>(V)) return true; // Otherwise, it must be an instruction... return !L->contains(cast<Instruction>(V)->getParent()); }

24

if (AllocationInst *AI = dyn_cast<AllocationInst>(Val)) { … } else if (CallInst *CI = dyn_cast<CallInst>(Val)) { … } else if …

slide-25
SLIDE 25

Cast Study: LLVM Compiler Infrastructure

25

Inheritance diagram: class CallInst

slide-26
SLIDE 26

Profiling the LLVM Compiler Infrastructure

Small workload: clang++ Hello.cpp

5.5 million dyn_cast<T>/isa<T> operations

74% failed

Large workload: clang selfie.c # 10K LoC

93.7 million dyn_cast<T>/isa<T> operations

78% failed

26

slide-27
SLIDE 27

Dynamic subtype tests often fail

But fast path is optimized for successful tests L

27

Takeaway: In some workloads…

slide-28
SLIDE 28

Can we fail fast when linear search is likely?

(with no overhead for the current fast path)

28

slide-29
SLIDE 29

Solution: Bl Bloom

  • om Filters

29

slide-30
SLIDE 30

Fail-Fast using Bloom Filters

For each type T:

α(T) := k distinct integers, chosen randomly from [1..m] β(T) := α(T) ∪ α(S1) ∪ α(S2) ∪ … ∪ α(Sn)

where S1, S2, … Sn are all the (transitive) super-types of T

Invariant:

T <: S ⇒ α(S) ⊆ β(T)

30

S T A B

α = {1, 3} α = {7, 9} α = {11, 4}

X

α = {5, 8} α = {1, 6} β = {1, 3, 4, 6, 7, 9, 11}

Y

α = {7, 11} X <: T ? No Y <: T ? Maybe

slide-31
SLIDE 31

Fail-Fast using Bloom Filters

For each type T:

α(T) := compile_time_random(parity=k) // m-bit integer β(T) := α(T) | α(S1) | α(S2) | …| α(Sn)

where S1, S2, … Sn are all the (transitive) super-types of T

Invariant:

T <: S ⇒ α(S) & β(T) = α(S)

31

S T A B

α = 0x000000000101 α = 0x000101000000 α = 0x010000001000

X

α = 0x000010010000 α = 0x000000100001 β = 0x010101101101

Y

α = 0x010001000000 X <: T ? No Y <: T ? Maybe

slide-32
SLIDE 32

Fail-Fast using Bloom Filters

32

Worst-case only when false positive in bloom filters

slide-33
SLIDE 33

Choosing parameters

m = size of machine word k = parity ?? n = num. of transitive supertypes

33

False positive rate:

slide-34
SLIDE 34

Preliminary Evaluation (JVM HotSpot)

34

slide-35
SLIDE 35

Preliminary Evaluation (JVM HotSpot)

35

slide-36
SLIDE 36

Preliminary Evaluation (JVM HotSpot)

  • bj match {

case x:A => x.method_on_A() case y:B => y.method_on_B() case z:C => z.method_on_C() … } if (obj instanceof A) { A x = (A) obj; x.method_on_A(); } else if (obj instanceof B) { B y = (B) obj; y.method_on_B(); } else if (obj instanceof C) { C z = (C) obj; z.method_on_C(); } …

36

Compile to JVM

slide-37
SLIDE 37

Preliminary Evaluation (JVM HotSpot)

37

Rewrite if T is a secondary type

slide-38
SLIDE 38

Preliminary Evaluation (JVM HotSpot)

38

slide-39
SLIDE 39

Preliminary Evaluation (JVM HotSpot)

39

trait Base trait A extends Base { def method_on_A(): Int } trait B extends Base { def method_on_B(): Int }

  • bject objA extends traitA { … }
  • bject objB extends traitB { … }
  • bj = chooseRandom({objA, objB})
  • bj match {

case x:A => x.method_on_A() case y:B => y.method_on_B() }

slide-40
SLIDE 40

Preliminary Evaluation (JVM HotSpot)

40

trait Base trait A extends Base { def method_on_A(): Int } trait B extends Base { def method_on_B(): Int } …

  • bject objA extends traitA { … }
  • bject objB extends traitB { … }

  • bj = chooseRandom({objA, objB, …})
  • bj match {

case x:A => x.method_on_A() case y:B => y.method_on_B() case z:C => z.method_on_C() case u:D => u.method_on_D() case v:E => v.method_on_E() }

slide-41
SLIDE 41

Preliminary Evaluation (JVM HotSpot)

41

  • bj match {

case x:A => x.method_on_A() case y:B => y.method_on_B() case z:C => z.method_on_C() case u:D => u.method_on_D() case v:E => v.method_on_E() … case q:H => q.method_on_H() }

trait Base trait A extends Base { def method_on_A(): Int } trait B extends Base { def method_on_B(): Int } …

  • bject objA extends traitA { … }
  • bject objB extends traitB { … }

  • bj = chooseRandom({objA, objB, …})
slide-42
SLIDE 42

Preliminary Evaluation (JVM HotSpot)

42

  • bj match {

case x:A => x.method_on_A() case y:B => y.method_on_B() case z:C => z.method_on_C() case u:D => u.method_on_D() case v:E => v.method_on_E() … case q:H => q.method_on_H() }

trait Base extends N1, N2, N3, … N10 trait A extends Base { def method_on_A(): Int } trait B extends Base { def method_on_B(): Int } …

  • bject objA extends traitA { … }
  • bject objB extends traitB { … }

  • bj = chooseRandom({objA, objB, …})
slide-43
SLIDE 43

Summary

Dynamic subtype tests often fail (in some workloads) Worst-case linear search occurs (in production VMs) Bloom filters can enable fail-fast refutations (high probability)

expected constant time + constant space + multiple inheritance + open hierarchy

43