Skip to content

Commit

Permalink
Switch to single-AppDomain execution
Browse files Browse the repository at this point in the history
As I thought, remote method invocation seems to have been the major
bottleneck in /serialize.  Using a lighter-weight isolation protocol
has improved performance:

(time mono-sgen run/Niecza.exe -C CORE)
master:  user    0m30.898s
before:  user    1m26.453s
now:     user    0m29.246s

The new procedure uses only Assembly-level isolation: runtime
assemblies are renamed to have names starting with Run.; so CORE.dll
is the compiler's CORE and Run.CORE.dll is the user's.  This seemed
preferable over the reverse to avoid name clashes with perversely
named user modules.
  • Loading branch information
sorear committed Oct 26, 2011
1 parent 4494faa commit 9083142
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 43 deletions.
8 changes: 4 additions & 4 deletions Makefile
Expand Up @@ -20,14 +20,14 @@ srcunits=CClass CgOp Op OpHelpers Sig RxOp STD NieczaGrammar Metamodel \
NieczaPassSimplifier OptBeta NieczaPathSearch NieczaBackendDotnet \
NieczaCompiler GetOptLong

all: run/Niecza.exe obj/Kernel.dll obj/CORE.dll
all: run/Niecza.exe obj/Run.Kernel.dll obj/Run.CORE.dll
@git describe --tags > VERSION

$(patsubst %,boot/obj/%.nam,$(srcunits)): boot/obj/%.nam: .fetch-stamp src/%.pm6 boot/obj/CORE.nam
cd src && $(RUN_CLR) ../boot/run/Niecza.exe -Bnam -C $*
$(RUN_CLR) boot/obj/CLRBackend.exe boot/obj $*.nam $*.dll 0

obj/CORE.dll: run/Niecza.exe obj/Kernel.dll lib/CORE.setting
obj/Run.CORE.dll: run/Niecza.exe obj/Run.Kernel.dll lib/CORE.setting
$(RUN_CLR) run/Niecza.exe -C CORE

run/Niecza.exe: .fetch-stamp $(patsubst %,boot/obj/%.nam,$(srcunits)) src/niecza
Expand All @@ -48,8 +48,8 @@ run/Niecza.exe: .fetch-stamp $(patsubst %,boot/obj/%.nam,$(srcunits)) src/niecza
boot/obj/CompilerBlob.dll: .fetch-stamp src/CompilerBlob.cs
$(CSC) /target:library /out:boot/obj/CompilerBlob.dll /r:Kernel \
/lib:boot/obj src/CompilerBlob.cs
obj/Kernel.dll: $(patsubst %,lib/%,$(cskernel))
$(CSC) /target:exe /out:obj/Kernel.dll /lib:obj /unsafe+ \
obj/Run.Kernel.dll: $(patsubst %,lib/%,$(cskernel))
$(CSC) /target:exe /out:obj/Run.Kernel.dll /lib:obj /unsafe+ \
$(patsubst %,lib/%,$(cskernel))

perl5: obj/Perl5Interpreter.dll obj/p5embed.so
Expand Down
8 changes: 2 additions & 6 deletions lib/Builtins.cs
Expand Up @@ -1628,16 +1628,12 @@ public partial class Builtins {
System.Diagnostics.Process.Start(file, args).WaitForExit();
}

[TrueGlobal] internal static AppDomain up_domain;
[TrueGlobal] static System.Collections.IDictionary upcall_receiver;
[TrueGlobal] internal static System.Collections.IDictionary upcall_receiver;
internal static object UpCall(object[] args) {
if (upcall_receiver == null)
upcall_receiver = (System.Collections.IDictionary)
up_domain.CreateInstanceAndUnwrap("CompilerBlob", "Niecza.UpcallReceiver");
return upcall_receiver[args];
}
public static Frame simple_eval(Frame th, Variable str) {
if (up_domain == null)
if (upcall_receiver == null)
return Kernel.Die(th, "Cannot eval; no compiler available");
SubInfo outer = th.caller.info;
object r = UpCall(new object[] { "eval",
Expand Down
10 changes: 8 additions & 2 deletions lib/CodeGen.cs
Expand Up @@ -3420,6 +3420,11 @@ class NamProcessor {
}

public class Backend {
[TrueGlobal]
public static string obj_dir = AppDomain.CurrentDomain.BaseDirectory;
[TrueGlobal]
public static string prefix = (typeof(Backend).Assembly.GetName().Name == "Kernel") ? "" : "Run.";

public static string LocStr(string fo, int lo, string fn, int ln) {
return fn == fo ? " (see line " + lo + ")" :
" (see " + fo + " line " + lo + ")";
Expand Down Expand Up @@ -3613,8 +3618,9 @@ public class DowncallReceiver : CallReceiver {
object o = Handle.Unbox(args[1]);
return (o is SubInfo) ? "sub" : (o is RuntimeUnit) ? "unit" :
(o is STable) ? "type" : (o is Frame) ? "frame" : "unknown";
} else if (cmd == "set_parent") {
Builtins.up_domain = (AppDomain)args[1];
} else if (cmd == "set_binding") {
Backend.obj_dir = (string)args[1];
Builtins.upcall_receiver = (System.Collections.IDictionary)args[2];
return null;
} else if (cmd == "push_compartment") {
Compartment.Push();
Expand Down
9 changes: 0 additions & 9 deletions lib/CrossDomainReceiver.cs

This file was deleted.

14 changes: 7 additions & 7 deletions lib/Kernel.cs
Expand Up @@ -593,7 +593,7 @@ public sealed class RuntimeUnit : IFreeze {
if (name == "CORE")
Kernel.CreateBasicTypes();

this.asm_name = name.Replace("::", ".");
this.asm_name = Backend.prefix + name.Replace("::", ".");
this.dll_name = asm_name + (main ? ".exe" : ".dll");
our_subs = new List<SubInfo>();
}
Expand All @@ -608,7 +608,7 @@ public sealed class RuntimeUnit : IFreeze {
new AssemblyName(asm_name),
(runnow ? AssemblyBuilderAccess.Run :
AssemblyBuilderAccess.Save),
AppDomain.CurrentDomain.BaseDirectory);
Backend.obj_dir);
eu.mod_builder = runnow ?
eu.asm_builder.DefineDynamicModule(asm_name) :
eu.asm_builder.DefineDynamicModule(asm_name, dll_name);
Expand Down Expand Up @@ -636,7 +636,7 @@ public sealed class RuntimeUnit : IFreeze {
typeof(void), new Type[] { typeof(string[]) });
var il = mainb.GetILGenerator();

il.Emit(OpCodes.Ldstr, asm_name);
il.Emit(OpCodes.Ldstr, name);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(Kernel).GetMethod("MainHandler"));
il.Emit(OpCodes.Ret);
Expand All @@ -663,7 +663,7 @@ public sealed class RuntimeUnit : IFreeze {
public void Save() {
EmitUnit eu = GenerateCode(false);
eu.asm_builder.Save(dll_name);
reg.SaveUnit(asm_name, this);
reg.SaveUnit(name, this);
}

internal void SetConstants() {
Expand Down Expand Up @@ -856,7 +856,7 @@ public sealed class RuntimeUnit : IFreeze {

n.name = tb.String();
string[] srcinfo = tb.Strings();
if (Builtins.up_domain != null) {
if (Builtins.upcall_receiver != null) {
object[] args = new object[srcinfo.Length + 1];
Array.Copy(srcinfo, 0, args, 1, srcinfo.Length);
args[0] = "check_dated";
Expand All @@ -872,8 +872,8 @@ public sealed class RuntimeUnit : IFreeze {

n.depended_units = new HashSet<RuntimeUnit>(tb.RefsA<RuntimeUnit>());

n.assembly = Assembly.LoadFrom(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, n.dll_name));
n.type = tb.type = n.assembly.GetType(n.name, true);
n.assembly = Assembly.Load(n.asm_name);
n.type = tb.type = n.assembly.GetType(n.asm_name, true);

int ncon = tb.Int();
n.constants = new Dictionary<object,FieldInfo>();
Expand Down
9 changes: 5 additions & 4 deletions lib/Serialize.cs
Expand Up @@ -3,6 +3,7 @@
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Text;
using Niecza.CLRBackend;

// Here in Niecza we have four different kinds of unit scopes:
//
Expand Down Expand Up @@ -111,8 +112,8 @@ struct ObjRef {
if (units.TryGetValue(name, out su))
return su;

string file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
name + ".ser");
string file = Path.Combine(Backend.obj_dir, Backend.prefix +
name.Replace("::",".") + ".ser");
byte[] bytes = File.ReadAllBytes(file);

su = new SerUnit();
Expand Down Expand Up @@ -163,8 +164,8 @@ struct ObjRef {
throw new InvalidOperationException("unit " +name+ " exists");

bool success = false;
string file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
name + ".ser");
string file = Path.Combine(Backend.obj_dir, Backend.prefix +
name.Replace("::",".") + ".ser");

FreezeBuffer fb = new FreezeBuffer(this, su);

Expand Down
34 changes: 23 additions & 11 deletions src/CompilerBlob.cs
@@ -1,4 +1,5 @@
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Text;
Expand Down Expand Up @@ -45,29 +46,40 @@ public class UpcallReceiver : CallReceiver {
}

public class Downcaller {
static AppDomain subDomain;
internal static Variable upcall_cb;
static IDictionary responder;
static P6any UnitP, StaticSubP, TypeP;
static string obj_dir;

// let the CLR load assemblies from obj/ too
static Assembly ObjLoader(object source, ResolveEventArgs e) {
string name = e.Name;
if (name.IndexOf(',') >= 0)
name = name.Substring(0, name.IndexOf(','));
string file = Path.Combine(obj_dir, name + ".dll");
if (File.Exists(file))
return Assembly.LoadFrom(file);
else
return null;
}
// Better, but still fudgy. Relies too much on path structure.
public static void InitSlave(Variable cb, Variable unit,
Variable staticSub, Variable type) {
if (subDomain != null) return;
if (responder != null) return;

UnitP = unit.Fetch();
StaticSubP = staticSub.Fetch();
TypeP = type.Fetch();

AppDomainSetup ads = new AppDomainSetup();
string obj = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.Combine("..", "obj")));
ads.ApplicationBase = obj;
string backend = Path.Combine(obj, "Kernel.dll");
subDomain = AppDomain.CreateDomain("zyg", null, ads);
obj_dir = Path.GetFullPath(Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
Path.Combine("..", "obj")));
AppDomain.CurrentDomain.AssemblyResolve += ObjLoader;

upcall_cb = cb;
responder = (IDictionary)
subDomain.CreateInstanceFromAndUnwrap(backend,
"Niecza.CLRBackend.DowncallReceiver");
RawDowncall("set_parent", AppDomain.CurrentDomain);
responder = (IDictionary) Activator.CreateInstance(Type.GetType(
"Niecza.CLRBackend.DowncallReceiver,Run.Kernel", true));
RawDowncall("set_binding", obj_dir, new UpcallReceiver());
}
public static object RawDowncall(params object[] args) {
return responder[args];
Expand Down

0 comments on commit 9083142

Please sign in to comment.