Accelerating Ruby with LLVM

135
Accelerating Ruby with LLVM Oct 2, 2009 Evan Phoenix Tuesday, October 6, 2009

Transcript of Accelerating Ruby with LLVM

Page 1: Accelerating Ruby with LLVM

Accelerating Rubywith LLVM

Oct 2, 2009

Evan Phoenix

Tuesday, October 6, 2009

Page 2: Accelerating Ruby with LLVM

RUBY

Tuesday, October 6, 2009

Page 3: Accelerating Ruby with LLVM

Strongly, dynamically typed

RUBY

Tuesday, October 6, 2009

Page 4: Accelerating Ruby with LLVM

Unified Model

RUBY

Tuesday, October 6, 2009

Page 5: Accelerating Ruby with LLVM

Everything is an object

RUBY

Tuesday, October 6, 2009

Page 6: Accelerating Ruby with LLVM

3.class # => FixnumRUBY

Tuesday, October 6, 2009

Page 7: Accelerating Ruby with LLVM

Every code context is equal

RUBY

Tuesday, October 6, 2009

Page 8: Accelerating Ruby with LLVM

Every context is a method

RUBY

Tuesday, October 6, 2009

Page 9: Accelerating Ruby with LLVM

Garbage Collected

RUBY

Tuesday, October 6, 2009

Page 10: Accelerating Ruby with LLVM

A lot of syntax

RUBY

Tuesday, October 6, 2009

Page 11: Accelerating Ruby with LLVM

Every code context is equalEvery context is a method

Garbage collectedA lot of syntax

RUBYStrongly, dynamically typed

Unified modelEverything is an object

3.class

Tuesday, October 6, 2009

Page 12: Accelerating Ruby with LLVM

Rubinius

Tuesday, October 6, 2009

Page 13: Accelerating Ruby with LLVM

Started in 2006

Rubinius

Tuesday, October 6, 2009

Page 14: Accelerating Ruby with LLVM

Build a ruby environment for fun

Rubinius

Tuesday, October 6, 2009

Page 15: Accelerating Ruby with LLVM

Unlike most “scripting” languages,write as much in ruby as possible

Rubinius

Tuesday, October 6, 2009

Page 16: Accelerating Ruby with LLVM

Core functionality of perl/python/ruby in C, NOT in their respective language.

Rubinius

Tuesday, October 6, 2009

Page 17: Accelerating Ruby with LLVM

C => ruby => C => rubyRubinius

Tuesday, October 6, 2009

Page 18: Accelerating Ruby with LLVM

Language boundaries suck

Rubinius

Tuesday, October 6, 2009

Page 19: Accelerating Ruby with LLVM

Started in 2006Built for fun

Turtles all the way down

Rubinius

Tuesday, October 6, 2009

Page 20: Accelerating Ruby with LLVM

Evolution

Tuesday, October 6, 2009

Page 21: Accelerating Ruby with LLVM

100% ruby prototype running on 1.8

Evolution

Tuesday, October 6, 2009

Page 22: Accelerating Ruby with LLVM

Hand translated VM to C

Evolution

Tuesday, October 6, 2009

Page 23: Accelerating Ruby with LLVM

Rewrote VM in C++

Evolution

Tuesday, October 6, 2009

Page 24: Accelerating Ruby with LLVM

Switch away from stackless

Evolution

Tuesday, October 6, 2009

Page 25: Accelerating Ruby with LLVM

Experimented with handwrittenassembler for x86

Evolution

Tuesday, October 6, 2009

Page 26: Accelerating Ruby with LLVM

Switch to LLVM for JIT

Evolution

Tuesday, October 6, 2009

Page 27: Accelerating Ruby with LLVM

Evolution100% ruby prototype

Hand translated VM to CRewrote VM in C++

Switch away from stacklessExperiment with assembler

Switch to LLVM for JIT

Tuesday, October 6, 2009

Page 28: Accelerating Ruby with LLVM

Features

Tuesday, October 6, 2009

Page 29: Accelerating Ruby with LLVM

Bytecode VM

Features

Tuesday, October 6, 2009

Page 30: Accelerating Ruby with LLVM

Simple interface to native code

Features

Tuesday, October 6, 2009

Page 31: Accelerating Ruby with LLVM

Accurate, generational garbage collector

Features

Tuesday, October 6, 2009

Page 32: Accelerating Ruby with LLVM

Integrated FFI API

Features

Tuesday, October 6, 2009

Page 33: Accelerating Ruby with LLVM

FeaturesBytecode VM

Interface to native codeGenerational GC

Integrated FFI

Tuesday, October 6, 2009

Page 34: Accelerating Ruby with LLVM

Benchmarks

Tuesday, October 6, 2009

Page 35: Accelerating Ruby with LLVM

def foo() ary = [] 100.times { |i| ary << i }end

300,000 timesTuesday, October 6, 2009

Page 36: Accelerating Ruby with LLVM

0

2.25

4.5

6.75

9

1.8 1.9 rbx rbx jit rbx jit +blocks

2.59

3.60

5.90

5.30

8.02

Seconds

Tuesday, October 6, 2009

Page 37: Accelerating Ruby with LLVM

def foo() hsh = {} 100.times { |i| hsh[i] = 0 }end

100,000 timesTuesday, October 6, 2009

Page 38: Accelerating Ruby with LLVM

0

7.5

15

22.5

30

1.8 1.9 rbx rbx jit rbx jit +blocks

12.0112.54

25.36

5.264.85

Seconds

Tuesday, October 6, 2009

Page 39: Accelerating Ruby with LLVM

def foo() hsh = { 47 => true } 100.times { |i| hsh[i] }end

100,000 timesTuesday, October 6, 2009

Page 40: Accelerating Ruby with LLVM

0

1.75

3.5

5.25

7

1.8 1.9 rbx rbx jit rbx jit +blocks

2.662.68

6.26

2.09

3.64

Seconds

Tuesday, October 6, 2009

Page 41: Accelerating Ruby with LLVM

Early LLVM Usage

Tuesday, October 6, 2009

Page 42: Accelerating Ruby with LLVM

Compiled all methods up front

Early LLVM Usage

Tuesday, October 6, 2009

Page 43: Accelerating Ruby with LLVM

Simple opcode-to-function translationwith inlining

Early LLVM Usage

Tuesday, October 6, 2009

Page 44: Accelerating Ruby with LLVM

Startup went from 0.3s to 80s

Early LLVM Usage

Tuesday, October 6, 2009

Page 45: Accelerating Ruby with LLVM

Early LLVM UsageCompiled all methods upfront

Simple opcode-to-function translationStartup from 0.3s to 80s

Tuesday, October 6, 2009

Page 46: Accelerating Ruby with LLVM

True JIT

Tuesday, October 6, 2009

Page 47: Accelerating Ruby with LLVM

JIT Goals

True JIT

Tuesday, October 6, 2009

Page 48: Accelerating Ruby with LLVM

JIT Goals

Choose methods that benefit the most

True JIT

Tuesday, October 6, 2009

Page 49: Accelerating Ruby with LLVM

JIT Goals

Compiling has minimum impact on performance

True JIT

Tuesday, October 6, 2009

Page 50: Accelerating Ruby with LLVM

JIT Goals

Ability to make intelligent frontend decisions

True JIT

Tuesday, October 6, 2009

Page 51: Accelerating Ruby with LLVM

Choosing Methods

Tuesday, October 6, 2009

Page 52: Accelerating Ruby with LLVM

Simple call counters

Choosing Methods

Tuesday, October 6, 2009

Page 53: Accelerating Ruby with LLVM

When counter trips, the fun starts

Choosing Methods

Tuesday, October 6, 2009

Page 54: Accelerating Ruby with LLVM

Room for improvement

Choosing Methods

Tuesday, October 6, 2009

Page 55: Accelerating Ruby with LLVM

Room for improvement

Increment counters in loops

Choosing Methods

Tuesday, October 6, 2009

Page 56: Accelerating Ruby with LLVM

Room for improvement

Weigh different invocations differently

Choosing Methods

Tuesday, October 6, 2009

Page 57: Accelerating Ruby with LLVM

Simple countersTrip the counters, do it

Choosing Methods

Room for improvementIncrement in loopsWeigh invocations

Tuesday, October 6, 2009

Page 58: Accelerating Ruby with LLVM

Which Method?

Tuesday, October 6, 2009

Page 59: Accelerating Ruby with LLVM

Leaf methods trip quickly

Which Method?

Tuesday, October 6, 2009

Page 60: Accelerating Ruby with LLVM

Leaf methods trip quickly

Consider the whole callstack

Which Methods?

Tuesday, October 6, 2009

Page 61: Accelerating Ruby with LLVM

Leaf methods trip quickly

Pick a parent expecting inlining

Which Methods?

Tuesday, October 6, 2009

Page 62: Accelerating Ruby with LLVM

Leaf methods tripConsider the callstack

Find a parent

Which Method?

Tuesday, October 6, 2009

Page 63: Accelerating Ruby with LLVM

Minimal Impact

Tuesday, October 6, 2009

Page 64: Accelerating Ruby with LLVM

After the counters trip

Minimal Impact

Tuesday, October 6, 2009

Page 65: Accelerating Ruby with LLVM

Queue the method

Minimal Impact

Tuesday, October 6, 2009

Page 66: Accelerating Ruby with LLVM

Background thread drains queue

Minimal Impact

Tuesday, October 6, 2009

Page 67: Accelerating Ruby with LLVM

Frontend, passes, codegen in background

Minimal Impact

Tuesday, October 6, 2009

Page 68: Accelerating Ruby with LLVM

Install JIT’d function

Minimal Impact

Tuesday, October 6, 2009

Page 69: Accelerating Ruby with LLVM

Install JIT’d function

Requires GC interaction

Minimal Impact

Tuesday, October 6, 2009

Page 70: Accelerating Ruby with LLVM

Trip the countersQueue the method

Minimal ImpactCompile in backgroundInstall function pointer

Tuesday, October 6, 2009

Page 71: Accelerating Ruby with LLVM

Good Decisions

Tuesday, October 6, 2009

Page 72: Accelerating Ruby with LLVM

Naive translation yields fixed improvement

Good Decisions

Tuesday, October 6, 2009

Page 73: Accelerating Ruby with LLVM

Performance shifts to method dispatch

Good Decisions

Tuesday, October 6, 2009

Page 74: Accelerating Ruby with LLVM

Improve optimization horizon

Good Decisions

Tuesday, October 6, 2009

Page 75: Accelerating Ruby with LLVM

Inline using type feedback

Good Decisions

Tuesday, October 6, 2009

Page 76: Accelerating Ruby with LLVM

Naive translation sucksInline using type feedback

Good Decisions

Performance in dispatchImprove optimizations

Tuesday, October 6, 2009

Page 77: Accelerating Ruby with LLVM

Type Feedback

Tuesday, October 6, 2009

Page 78: Accelerating Ruby with LLVM

Frontend translates to IR

Type Feedback

Tuesday, October 6, 2009

Page 79: Accelerating Ruby with LLVM

Read InlineCache information

Type Feedback

Tuesday, October 6, 2009

Page 80: Accelerating Ruby with LLVM

InlineCaches contain profiling info

Type Feedback

Tuesday, October 6, 2009

Page 81: Accelerating Ruby with LLVM

Use profiling to drive inlining!

Type Feedback

Tuesday, October 6, 2009

Page 82: Accelerating Ruby with LLVM

Frontend generates IRReads InlineCaches

Type FeedbackInlineCaches have profiling

Use profiling to drive inlining!

Tuesday, October 6, 2009

Page 83: Accelerating Ruby with LLVM

Inlining

Tuesday, October 6, 2009

Page 84: Accelerating Ruby with LLVM

Profiling info shows a dominant class

Inlining

Tuesday, October 6, 2009

Page 85: Accelerating Ruby with LLVM

21%

1 class98%

Tuesday, October 6, 2009

Page 86: Accelerating Ruby with LLVM

Lookup method in compiler

Inlining

Tuesday, October 6, 2009

Page 87: Accelerating Ruby with LLVM

For native functions, emit direct call

Inlining

Tuesday, October 6, 2009

Page 88: Accelerating Ruby with LLVM

For FFI, inline conversions and call

Inlining

Tuesday, October 6, 2009

Page 89: Accelerating Ruby with LLVM

Emit direct calls if possible

InliningFind dominant class

Lookup method

Tuesday, October 6, 2009

Page 90: Accelerating Ruby with LLVM

Inlining Ruby

Tuesday, October 6, 2009

Page 91: Accelerating Ruby with LLVM

Policy decides on inlining

Inlining Ruby

Tuesday, October 6, 2009

Page 92: Accelerating Ruby with LLVM

Drive sub-frontend at call site

Inlining Ruby

Tuesday, October 6, 2009

Page 93: Accelerating Ruby with LLVM

All inlining occurs in the frontend

Inlining Ruby

Tuesday, October 6, 2009

Page 94: Accelerating Ruby with LLVM

Generated IR preserves runtime data

Inlining Ruby

Tuesday, October 6, 2009

Page 95: Accelerating Ruby with LLVM

Generated IR preserves runtime data

GC roots, backtraces, etc

Inlining Ruby

Tuesday, October 6, 2009

Page 96: Accelerating Ruby with LLVM

No AST between bytecode and IR

Inlining Ruby

Tuesday, October 6, 2009

Page 97: Accelerating Ruby with LLVM

No AST between bytecode and IR

Fast, but limits the ability to generate better IR

Inlining Ruby

Tuesday, October 6, 2009

Page 98: Accelerating Ruby with LLVM

Policy decidesDrive sub-frontend

Inlining RubyPreserve runtime dataGenerates fast, ugly IR

Tuesday, October 6, 2009

Page 99: Accelerating Ruby with LLVM

LLVM

Tuesday, October 6, 2009

Page 100: Accelerating Ruby with LLVM

IR uses operand stack

LLVM

Tuesday, October 6, 2009

Page 101: Accelerating Ruby with LLVM

IR uses operand stack

Highlevel data flow not in SSA

LLVM

Tuesday, October 6, 2009

Page 102: Accelerating Ruby with LLVM

IR uses operand stack

Passes eliminate redundencies

LLVM

Tuesday, October 6, 2009

Page 103: Accelerating Ruby with LLVM

IR uses operand stack

Makes GC stack marking easy

LLVM

Tuesday, October 6, 2009

Page 104: Accelerating Ruby with LLVM

IR uses operand stack

nocapture improves propagation

LLVM

Tuesday, October 6, 2009

Page 105: Accelerating Ruby with LLVM

Exceptions via sentinal value

LLVM

Tuesday, October 6, 2009

Page 106: Accelerating Ruby with LLVM

Exceptions via sentinal value

Nested handlers use branches for control

LLVM

Tuesday, October 6, 2009

Page 107: Accelerating Ruby with LLVM

Exceptions via sentinal value

Inlining exposes redundant checks

LLVM

Tuesday, October 6, 2009

Page 108: Accelerating Ruby with LLVM

Inline guards

LLVM

Tuesday, October 6, 2009

Page 109: Accelerating Ruby with LLVM

Inline guards

Simple type guards

LLVM

Tuesday, October 6, 2009

Page 110: Accelerating Ruby with LLVM

if(obj->class->class_id == <integer constant>) {

Tuesday, October 6, 2009

Page 111: Accelerating Ruby with LLVM

Inline guards

Custom AA pass for guard elimination

LLVM

Tuesday, October 6, 2009

Page 112: Accelerating Ruby with LLVM

Inline guards

Teach pointsToConstantMemory about...

LLVM

Tuesday, October 6, 2009

Page 113: Accelerating Ruby with LLVM

if(obj->class->class_id == <integer constant>) {

Tuesday, October 6, 2009

Page 114: Accelerating Ruby with LLVM

if(obj->class->class_id == <integer constant>) {

Tuesday, October 6, 2009

Page 115: Accelerating Ruby with LLVM

Maximizing constant propagation

LLVM

Tuesday, October 6, 2009

Page 116: Accelerating Ruby with LLVM

Maximizing constant propagation

Type failures shouldn’t contribute values

LLVM

Tuesday, October 6, 2009

Page 117: Accelerating Ruby with LLVM

if(obj->class->class_id == 0x33) { val = 0x7;} else { val = send_msg(state, obj, ...);}

Tuesday, October 6, 2009

Page 118: Accelerating Ruby with LLVM

if(obj->class->class_id == 0x33) { val = 0x7;} else { return uncommon(state);}

Tuesday, October 6, 2009

Page 119: Accelerating Ruby with LLVM

Maximizing constant propagation

Makes JIT similar to tracing

LLVM

Tuesday, October 6, 2009

Page 120: Accelerating Ruby with LLVM

Use overflow intrinsics

LLVM

Tuesday, October 6, 2009

Page 121: Accelerating Ruby with LLVM

Use overflow intrinsics

Custom pass to fold constants arguments

LLVM

Tuesday, October 6, 2009

Page 122: Accelerating Ruby with LLVM

AA knowledge for tagged pointers

LLVM

Tuesday, October 6, 2009

Page 123: Accelerating Ruby with LLVM

AA knowledge of tagged pointers

0x5 is 2 as a tagged pointer

LLVM

Tuesday, October 6, 2009

Page 124: Accelerating Ruby with LLVM

Not in SSA formSimplistic exceptions

Inlining guards

LLVMMaximize constants

Use overflowTagged pointer AA

Tuesday, October 6, 2009

Page 125: Accelerating Ruby with LLVM

Issues

Tuesday, October 6, 2009

Page 126: Accelerating Ruby with LLVM

How to link with LLVM?

Issues

Tuesday, October 6, 2009

Page 127: Accelerating Ruby with LLVM

How to link with LLVM?

An important SCM issue

Issues

Tuesday, October 6, 2009

Page 128: Accelerating Ruby with LLVM

Ugly, confusing IR from frontend

Issues

Tuesday, October 6, 2009

Page 129: Accelerating Ruby with LLVM

instcombine confuses basicaa

Issues

Tuesday, October 6, 2009

Page 130: Accelerating Ruby with LLVM

Operand stack confuses AA

Issues

Tuesday, October 6, 2009

Page 131: Accelerating Ruby with LLVM

Inability to communicate semantics

Issues

Tuesday, October 6, 2009

Page 132: Accelerating Ruby with LLVM

Object* new_object(state)

Tuesday, October 6, 2009

Page 133: Accelerating Ruby with LLVM

Returned pointer aliases nothing

Only modifies state

If return value is unused, remove the call

Semi-pure?

Tuesday, October 6, 2009

Page 134: Accelerating Ruby with LLVM

Ugly IRLinking with LLVM

IssuesAA confusion

Highlevel semantics

Tuesday, October 6, 2009