Integrating Haskell to .NET
description
Transcript of Integrating Haskell to .NET
Integrating Haskell to .NETIntegrating Haskell to .NET
André Santos / Monique Monteiro{alms,mlbm}@cin.ufpe.br
Rotor Workshop Sep 2005 - MSR
MotivationMotivation• Advantages of having Haskell Advantages of having Haskell
compiler for the .NET platformcompiler for the .NET platform– Easier interop with other languagesEasier interop with other languages– Stronger integration of Haskell to .NET Stronger integration of Haskell to .NET
components and toolscomponents and tools– PerformancePerformance– Limitation of previous implementationsLimitation of previous implementations
Related workRelated work• Previous Haskell implementations Previous Haskell implementations
for .NETfor .NET– Little or no documentationLittle or no documentation– Partial implementationsPartial implementations– (very) limited availability and (very) limited availability and
documentationdocumentation
• Implementation of other functional Implementation of other functional languages for .NETlanguages for .NET– Scheme, F#, SML.NET, Mondrian, etc.Scheme, F#, SML.NET, Mondrian, etc.– But Haskell is a But Haskell is a lazylazy functional language functional language
• Implementations of Haskell for the JVMImplementations of Haskell for the JVM
JVM x .NET CLRJVM x .NET CLR
Focus on OO LanguagesFocus on OO Languages
Focus on language Focus on language interoperabilityinteroperability
Support for tail callsSupport for tail calls
Support for function pointersSupport for function pointers
JVMJVM
●●○○○○○○
CLRCLR
○○●●●●●●
Main Design DecisionsMain Design Decisions
• Evaluation strategyEvaluation strategy– uses uses push/enter push/enter model (instead of model (instead of
eval/applyeval/apply))
• Data types representationData types representation– Use common container classes Use common container classes (instead of(instead of
Classes for each constructor of each Classes for each constructor of each Haskell datatype)Haskell datatype)
• Closure representationClosure representation
Closure RepresentationClosure Representation
• One class per closureOne class per closure– Large number of classes: (expected) high Large number of classes: (expected) high
overhead for class loading, verification overhead for class loading, verification routines, etc.routines, etc.
• Classes with Function PointersClasses with Function Pointers– Unverifiable codeUnverifiable code
• DelegatesDelegates– Type-safeType-safe– Better performance in .NET 2.0Better performance in .NET 2.0
One class / closureOne class / closurepublic class Closure_302 : Closure {
public Closure value; public static Closure enter(){ // if value is available // return it // otherwise evaluate specific closure code, update it and return it }}
public class Closure_302 : Closure {
public Closure value; public static Closure enter(){ // if value is available // return it // otherwise evaluate specific closure code, update it and return it }}
Common closure classCommon closure class public class Closure : Delegate { public Closure value;
public static IClosure Enter(){ // if value is available return it // otherwise save stacks’ state, evaluate (call to Delegate.Invoke), // update value, restore stacks’ state and return value ... } }
public class Closure : Delegate { public Closure value;
public static IClosure Enter(){ // if value is available return it // otherwise save stacks’ state, evaluate (call to Delegate.Invoke), // update value, restore stacks’ state and return value ... } }
Closure RepresentationClosure Representation
• Updateable closuresUpdateable closures– Keep a Keep a valuevalue field field
• Non-updateable closuresNon-updateable closures– Keep an Keep an arityarity field field– Keep a Keep a partial applicationpartial application field to field to
encapsulate received argumentsencapsulate received arguments
• Both keep the free variablesBoth keep the free variables– Set to null after updateSet to null after update
• Delegates point to the slow entry pointDelegates point to the slow entry point– Slow entry point X fast entry pointSlow entry point X fast entry point
Runtime System - ClosuresRuntime System - Closures
Example: Example: mapmap function function
map = {} \n {f,l} -> case l of Nil {} -> Nil {} Cons {x,xs} -> let fx = {f,x} \u {} -> f {x} fxs = {f,xs} \u {} -> map {f,xs}
in Cons {fx,fxs}
map = {} \n {f,l} -> case l of Nil {} -> Nil {} Cons {x,xs} -> let fx = {f,x} \u {} -> f {x} fxs = {f,xs} \u {} -> map {f,xs}
in Cons {fx,fxs}
Example: generated codeExample: generated codepublic static IClosure map(NonUpdatable clo){ ... }public static IClosure map(IClosure f,IClosure l){ Pack scrutinee = (Pack)l.Enter(); switch(scrutinee.tag){ case 1: return new Pack(1); case 2: Pack_2<IClosure,IClosure> cons =
(Pack_2<IClosure,IClosure>) scrutinee; IClosure x = cons.arg1; IClosure xs = cons.arg2; Updateable_2_FV fx_closure = new Updateable_2_FV(fx); fx_closure.fv1 = f; fx_closure.fv2 = x; Updateable_2_FV mfxs_closure = new Updateable_2_FV (mfxs); mfxs_closure.fv1 = f; mfxs_closure.fv2 = xs; return new Pack_2<IClosure,IClosure>(2, fx_closure, mfxs_closure); }}
public static IClosure map(NonUpdatable clo){ ... }public static IClosure map(IClosure f,IClosure l){ Pack scrutinee = (Pack)l.Enter(); switch(scrutinee.tag){ case 1: return new Pack(1); case 2: Pack_2<IClosure,IClosure> cons =
(Pack_2<IClosure,IClosure>) scrutinee; IClosure x = cons.arg1; IClosure xs = cons.arg2; Updateable_2_FV fx_closure = new Updateable_2_FV(fx); fx_closure.fv1 = f; fx_closure.fv2 = x; Updateable_2_FV mfxs_closure = new Updateable_2_FV (mfxs); mfxs_closure.fv1 = f; mfxs_closure.fv2 = xs; return new Pack_2<IClosure,IClosure>(2, fx_closure, mfxs_closure); }}
Slow entry point
Fast entry point
Nil tag
Cons tag
Implementation DecisionsImplementation Decisions
• Use existing optimizing compiler for HaskellUse existing optimizing compiler for Haskell– The Glasgow Haskell Compiler The Glasgow Haskell Compiler –– GHCGHC – STG Intermediate representation LanguageSTG Intermediate representation Language
• Generate code in CILGenerate code in CIL– No support for explicit tail-calls from C#No support for explicit tail-calls from C#– Cannot extend (Multicast)delegate class from C#Cannot extend (Multicast)delegate class from C#
Current StatusCurrent Status
• Support for parts of the Haskell Support for parts of the Haskell preludeprelude– (GHC) modules Base, List, Num, Show, (GHC) modules Base, List, Num, Show,
Real, Enum, …Real, Enum, …
• Currently performing tests and Currently performing tests and performance evaluation using some performance evaluation using some programs from Nofib Benchmark suiteprograms from Nofib Benchmark suite
• Limited performance results so farLimited performance results so far
Execution TimesExecution Times
GHC .NET Native GHC .NET/Native
Digits_e1 8.97 1.57 5.70
Digits_e2 13,6 2.62 5.18
Exp3 31.38 2.94 10.66
Primes 11.09 0.42 26.34
Queens 5.86 1.65 3.55
Wheel-sieve1 16.98 2.50 6.79
Wheel-sieve2 8.18 1.54 5.30
Tak 16.95 13.58 1.24
CAFs(top-level constants)
Some Profiling ResultsSome Profiling Results
• CAFs (updateable closure + value) CAFs (updateable closure + value) lead to long-lived objects (Gen 2) and lead to long-lived objects (Gen 2) and large memory consumptionlarge memory consumption
• Delegates are the largest objectsDelegates are the largest objects
• Delegate constructors have the Delegate constructors have the highest cost among runtime system highest cost among runtime system methodsmethods
Main DifficultiesMain Difficulties
• In-place updating is not supportedIn-place updating is not supported– It is not possible to store pointers to It is not possible to store pointers to
referencesreferences
• Tail-callsTail-calls– It’s not clear in which situations they are It’s not clear in which situations they are
supported by the execution systemsupported by the execution system• Delegates? Virtual methods? Method in other Delegates? Virtual methods? Method in other
assemblies?assemblies?
– ““Disabled in fully-trusted environments”Disabled in fully-trusted environments”
Main DifficultiesMain Difficulties
• CLR stack limitations:CLR stack limitations:– Push/enter approach requires the direct Push/enter approach requires the direct
management of a stack for handling calls management of a stack for handling calls to unknown functionsto unknown functions
– Separate stack(s) had to be implementedSeparate stack(s) had to be implemented– Possible solution: eval/apply approachPossible solution: eval/apply approach
Main DifficultiesMain Difficulties
• .NET 2.0 in Beta version.NET 2.0 in Beta version• Limited profiling tools for .NET 2.0Limited profiling tools for .NET 2.0• Rotor for .NET 2.0 is not available yetRotor for .NET 2.0 is not available yet• Calls to C routines in Prelude modulesCalls to C routines in Prelude modules
– implement .NET FFI conventionimplement .NET FFI convention
Next StepsNext Steps
• Fix the CAF problemFix the CAF problem• Evaluate changes in delegates Evaluate changes in delegates
inheritance inheritance • Investigate tail-calls and their Investigate tail-calls and their
limitations/overhead in .NET 2.0limitations/overhead in .NET 2.0• Extend Rotor with support for Extend Rotor with support for
ClosuresClosures• Investigate ways of exposing Haskell Investigate ways of exposing Haskell
code to other .NET languages and code to other .NET languages and vice-versavice-versa
Questions?Questions?
André Santos / Monique Monteiro{alms,mlbm}@cin.ufpe.br