Commit 33e433e5 authored by Kunshan Wang's avatar Kunshan Wang

A more configurable MicroVM constructor.

Tuning the heap size may reveal some GC bugs.
parent 533893e2
......@@ -68,7 +68,7 @@ static void init_jvm() {
exit(1);
}
new_ex_mid = (*env)->GetStaticMethodID(env, cinitiater_cls, new_ex_method, "(JJJ)J");
new_ex_mid = (*env)->GetStaticMethodID(env, cinitiater_cls, new_ex_method, "(Ljava/lang/String;)J");
if (new_ex_mid == NULL) {
printf("ERROR: method %s cannot be found.\n", new_ex_method);
......@@ -94,13 +94,22 @@ MuVM *mu_refimpl2_new() {
return (MuVM*)rv;
}
MuVM *mu_refimpl2_new_ex(int64_t heap_size, int64_t global_size, int64_t stack_size) {
MuVM *mu_refimpl2_new_ex(const char *gc_conf) {
if (jvm == NULL) {
init_jvm();
}
jstring conf_str = (*env)->NewStringUTF(env, gc_conf);
if (conf_str == NULL) {
printf("ERROR: Cannot convert gc_conf to Java String\n");
exit(1);
}
uintptr_t rv = (*env)->CallStaticLongMethod(env, cinitiater_cls, new_ex_mid,
heap_size, global_size, stack_size);
conf_str);
(*env)->DeleteLocalRef(env, conf_str);
return (MuVM*)rv;
}
......
......@@ -10,7 +10,7 @@ extern "C" {
#endif
MuVM *mu_refimpl2_new();
MuVM *mu_refimpl2_new_ex(int64_t heap_size, int64_t global_size, int64_t stack_size);
MuVM *mu_refimpl2_new_ex(const char *gc_conf);
void mu_refimpl2_close(MuVM *mvm);
......
......@@ -7,10 +7,18 @@
#include <refimpl2-start.h>
#include <muapi.h>
char *hw_string = "Hello world!\n";
const char *hw_string = "Hello world!\n";
const char *gc_conf =
"sosSize=524288\n"
"losSize=524288\n"
"globalSize=1048576\n"
"stackSize=32768\n"
;
int main() {
MuVM *mvm = mu_refimpl2_new_ex(1048576, 1048576, 32768);
MuVM *mvm = mu_refimpl2_new_ex(gc_conf);
MuCtx *ctx = mvm->new_context(mvm);
......
......@@ -4,4 +4,3 @@ def _assert_instance(obj, *tys):
if not any(isinstance(obj, ty) for ty in tys):
raise AssertionError("{} is not an instance of {}".format(obj,
" or ".join(map(str,tys))))
......@@ -5,17 +5,17 @@ from _libmuprivcommon import _assert_instance
def _is_str_like(v):
return isinstance(v, str) or isinstance(v, unicode)
def _encode_ascii(v):
def _encode(v, encoding):
_assert_instance(v, str, unicode)
if isinstance(v, unicode):
return v.encode("ascii")
return v.encode(encoding)
else:
return v
def _decode_ascii(v):
def _decode(v, encoding):
_assert_instance(v, str, unicode)
if isinstance(v, str):
return v.decode("ascii")
return v.decode(encoding)
else:
return v
......
......@@ -3,17 +3,17 @@ from _libmuprivcommon import _assert_instance
def _is_str_like(v):
return isinstance(v, str) or isinstance(v, bytes)
def _encode_ascii(v):
def _encode(v, encoding):
_assert_instance(v, bytes, str)
if isinstance(v, str):
return v.encode("ascii")
return v.encode(encoding)
else:
return v
def _decode_ascii(v):
def _decode(v, encoding):
_assert_instance(v, bytes, str)
if isinstance(v, bytes):
return v.decode("ascii")
return v.decode(encoding)
else:
return v
......
......@@ -36,7 +36,12 @@ Then a ``MuVM`` instance can be created from that object::
or::
mu = dll.mu_refimpl2_new_ex(HEAP_SIZE, GLOBAL_SIZE, STACK_SIZE)
mu = dll.mu_refimpl2_new_ex(
sosSize = 2*1024*1024,
losSize = 2*1024*1024,
globalSize = 4*1024*1024,
stackSize = 63*1024,
)
A ``MuCtx`` instance can be created from the ``MuVM`` object::
......@@ -670,7 +675,7 @@ class MuCtx(_StructOfMethodsWrapper):
Arguments:
bundle_str: a str or unicode as the text-based bundle.
"""
ascii_bundle = _priv._encode_ascii(bundle_str)
ascii_bundle = _priv._encode(bundle_str, "ascii")
return self.load_bundle_(ascii_bundle, len(ascii_bundle))
def load_hail(self, hail_str):
......@@ -679,7 +684,7 @@ class MuCtx(_StructOfMethodsWrapper):
Arguments:
bundle_str: a str or unicode as the text-based HAIL script.
"""
ascii_bundle = _priv._encode_ascii(hail_str)
ascii_bundle = _priv._encode(hail_str, "ascii")
return self.load_hail_(ascii_bundle, len(ascii_bundle))
def handle_from_int(self, value, length):
......@@ -844,13 +849,13 @@ def _to_low_level_type(ty):
def _to_low_level_arg(arg):
return (arg._to_low_level_arg() if isinstance(arg, _LowLevelTypeWrapper) else
_priv._encode_ascii(arg) if _priv._is_str_like(arg) else
_priv._encode(arg, "ascii") if _priv._is_str_like(arg) else
arg)
def _from_low_level_retval(restype, low_level_rv, self):
return (restype._from_low_level_retval(low_level_rv, self)
if isinstance(restype, type) and issubclass(restype, _LowLevelTypeWrapper) else
_priv._decode_ascii(low_level_rv) if _priv._is_str_like(low_level_rv) else
_priv._decode(low_level_rv, "ascii") if _priv._is_str_like(low_level_rv) else
low_level_rv)
def _make_high_level_method(name, expected_nargs, restype, argtypes):
......@@ -1119,8 +1124,7 @@ class MuRefImpl2StartDLL(object):
dll.mu_refimpl2_new.argtypes = []
dll.mu_refimpl2_new_ex.restype = CPtrMuVM
dll.mu_refimpl2_new_ex.argtypes = [ctypes.c_int64,
ctypes.c_int64, ctypes.c_int64]
dll.mu_refimpl2_new_ex.argtypes = [ctypes.c_char_p]
dll.mu_refimpl2_close.restype = None
dll.mu_refimpl2_close.argtypes = [CPtrMuVM]
......@@ -1132,13 +1136,17 @@ class MuRefImpl2StartDLL(object):
ptr = self.dll.mu_refimpl2_new()
return MuVM(ptr, self)
def mu_refimpl2_new_ex(self, heap_size, global_size, stack_size):
"""Create a MuVM instance using custom heap/global/stack sizes.
def mu_refimpl2_new_ex(self, **kwargs):
"""Create a MuVM instance using custom configuration.
heap_size, global_size, stack_size are the sizes of the GC heap, global
memory and each stack. All of them are in bytes.
Currently supported keyword arguments:
sosSize: small object space size (bytes, must be 4096-byte aligned)
losSize: large object space size (bytes, must be 4096-byte aligned)
globalSize: global space size (bytes, must be 4096-byte aligned)
stackSize: stack size (bytes)
"""
ptr = self.dll.mu_refimpl2_new_ex(heap_size, global_size, stack_size)
conf = "".join("{}={}\n".format(k,v) for k,v in kwargs.items())
ptr = self.dll.mu_refimpl2_new_ex(_priv._encode(conf, "utf8"))
return MuVM(ptr, self)
def mu_refimpl2_close(self, muvm):
......
......@@ -5,7 +5,12 @@ import unittest
from libmu import *
dll = MuRefImpl2StartDLL(u"../cbinding/libmurefimpl2start.so")
mu = dll.mu_refimpl2_new()
mu = dll.mu_refimpl2_new_ex(
sosSize = 2*1024*1024,
losSize = 2*1024*1024,
globalSize = 4*1024*1024,
stackSize = 63*1024,
)
with mu.new_context() as ctx:
ctx.load_bundle("""
......
......@@ -45,8 +45,7 @@ public class CInitiater {
/** Called by the native program, this function creates a Mu instance. */
static long mu_refimpl2_new() {
configureLog();
MicroVM mvm = new MicroVM(MicroVM$.MODULE$.DEFAULT_HEAP_SIZE(), MicroVM$.MODULE$.DEFAULT_GLOBAL_SIZE(),
MicroVM$.MODULE$.DEFAULT_STACK_SIZE());
MicroVM mvm = MicroVM$.MODULE$.apply();
long fak = NativeClientSupport$.MODULE$.exposeMicroVM(mvm);
return fak;
}
......@@ -55,9 +54,9 @@ public class CInitiater {
* Called by the native program, this function creates a Mu instance with
* extra arguments.
*/
static long mu_refimpl2_new_ex(long heap_size, long global_size, long stack_size) {
static long mu_refimpl2_new_ex(String gcConfString) {
configureLog();
MicroVM mvm = new MicroVM(heap_size, global_size, stack_size);
MicroVM mvm = MicroVM$.MODULE$.apply(gcConfString);
long fak = NativeClientSupport$.MODULE$.exposeMicroVM(mvm);
return fak;
}
......
......@@ -12,31 +12,72 @@ import uvm.refimpl.mem.TypeSizes.Word
import uvm.refimpl.nat.NativeCallHelper
import uvm.staticanalysis.StaticAnalyzer
object GCConf {
val ReComment = """^\s*#.*$""".r
val ReBlank = """^\s*$""".r
val ReConf = """^\s*(\w+)=(\w+)\s*$""".r
def apply(confStr: String): GCConf = {
var sosSize = MicroVM.DEFAULT_SOS_SIZE
var losSize = MicroVM.DEFAULT_LOS_SIZE
var globalSize = MicroVM.DEFAULT_GLOBAL_SIZE
var stackSize = MicroVM.DEFAULT_GLOBAL_SIZE
confStr.lines foreach {
case ReComment() =>
case ReBlank() =>
case ReConf(key, value) => {
key match {
case "sosSize" => sosSize = value.toLong
case "losSize" => losSize = value.toLong
case "globalSize" => globalSize = value.toLong
case "stackSize" => stackSize = value.toLong
case _ => throw new UvmRefImplException("Unrecognized option %s".format(key))
}
}
}
new GCConf(sosSize, losSize, globalSize, stackSize)
}
}
class GCConf(
val sosSize: Word = MicroVM.DEFAULT_SOS_SIZE,
val losSize: Word = MicroVM.DEFAULT_LOS_SIZE,
val globalSize: Word = MicroVM.DEFAULT_GLOBAL_SIZE,
val stackSize: Word = MicroVM.DEFAULT_STACK_SIZE)
object MicroVM {
val DEFAULT_HEAP_SIZE: Word = 4L * 1024L * 1024L; // 4MiB
val DEFAULT_SOS_SIZE: Word = 2L * 1024L * 1024L; // 2MiB
val DEFAULT_LOS_SIZE: Word = 2L * 1024L * 1024L; // 2MiB
val DEFAULT_GLOBAL_SIZE: Word = 1L * 1024L * 1024L; // 1MiB
val DEFAULT_STACK_SIZE: Word = 63L * 1024L; // 60KiB per stack
val FIRST_CLIENT_USABLE_ID: Int = 65536
}
class MicroVM(heapSize: Word = MicroVM.DEFAULT_HEAP_SIZE,
globalSize: Word = MicroVM.DEFAULT_GLOBAL_SIZE,
stackSize: Word = MicroVM.DEFAULT_STACK_SIZE) {
def apply(): MicroVM = {
val gcConf = new GCConf()
new MicroVM(gcConf)
}
def apply(confStr: String): MicroVM = {
val gcConf = GCConf(confStr)
new MicroVM(gcConf)
}
}
class MicroVM(gcConf: GCConf) {
// implicitly injected resources
private implicit val microVM = this
val globalBundle = new GlobalBundle()
val constantPool = new ConstantPool()
val memoryManager = new MemoryManager(heapSize, globalSize, stackSize)
val memoryManager = new MemoryManager(gcConf)
private implicit val memorySupport = memoryManager.memorySupport
implicit val nativeCallHelper = new NativeCallHelper()
val threadStackManager = new ThreadStackManager()
val trapManager = new TrapManager()
val contexts = new HashSet[MuCtx]()
......@@ -57,7 +98,7 @@ class MicroVM(heapSize: Word = MicroVM.DEFAULT_HEAP_SIZE,
}
// Some internal constants needed by the HAIL loader
for (c <- Seq(NULL_REF_VOID, NULL_IREF_VOID, NULL_FUNCREF_VV, NULL_THREADREF, NULL_STACKREF)) {
globalBundle.constantNs.add(c)
constantPool.addGlobalVar(c)
......@@ -69,7 +110,7 @@ class MicroVM(heapSize: Word = MicroVM.DEFAULT_HEAP_SIZE,
*/
def addBundle(bundle: TrantientBundle) {
staticAnalyzer.checkBundle(bundle, Some(globalBundle))
globalBundle.merge(bundle);
for (gc <- bundle.globalCellNs.all) {
......
......@@ -3,25 +3,46 @@ package uvm.refimpl.mem
import uvm.refimpl._
import TypeSizes._
import uvm.refimpl.mem.simpleimmix._
import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.Logger
class MemoryManager(val heapSize: Word, val globalSize: Word, val stackSize: Word)(implicit microVM: MicroVM) {
object MemoryManager {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
}
class MemoryManager(val gcConf: GCConf)(implicit microVM: MicroVM) {
import MemoryManager._
val totalMemorySize = heapSize + globalSize
logger.info("sosSize=%d, losSize=%d, globalSize=%d, stackSize=%d".format(
gcConf.sosSize, gcConf.losSize, gcConf.globalSize, gcConf.stackSize))
require(gcConf.sosSize % 4096L == 0, "Small object space size must be 4096 bytes aligned. actual size: %d".format(gcConf.sosSize))
require(gcConf.losSize % 4096L == 0, "Large object space size must be 4096 bytes aligned. actual size: %d".format(gcConf.losSize))
require(gcConf.globalSize % 4096L == 0, "Global space size must be 4096 bytes aligned. actual size: %d".format(gcConf.globalSize))
val totalMemorySize = gcConf.sosSize + gcConf.losSize + gcConf.globalSize
// Allocate slightly more memory to meet the SimpleImmixSpace's alignment requirement.
implicit val memorySupport = new MemorySupport(totalMemorySize + SimpleImmixSpace.BLOCK_SIZE)
val memoryBegin = memorySupport.muMemoryBegin
val heapBegin = TypeSizes.alignUp(memoryBegin, SimpleImmixSpace.BLOCK_SIZE)
val globalBegin = heapBegin + gcConf.sosSize + gcConf.losSize
val globalEnd = globalBegin + gcConf.globalSize
logger.info(("Mu memory allocated.\n memoryBegin=%d 0x%x\n heapBegin=%d 0x%x\n" +
" globalBegin=%d 0x%x\n memory end=%d 0x%x").format(
memoryBegin, memoryBegin, heapBegin, heapBegin, globalBegin, globalBegin, globalEnd, globalEnd
))
val heap = new SimpleImmixHeap(heapBegin, heapSize)
val globalMemory = new GlobalMemory(heapBegin + heapSize, globalSize)
val heap = new SimpleImmixHeap(heapBegin, gcConf.sosSize, gcConf.losSize)
val globalMemory = new GlobalMemory(globalBegin, gcConf.globalSize)
def makeMutator(): Mutator = heap.makeMutator()
def makeStackMemory(mutator: Mutator): StackMemory = {
val objRef = mutator.newHybrid(InternalTypes.BYTE_ARRAY, stackSize)
val stackMemory = new StackMemory(objRef, stackSize)
val objRef = mutator.newHybrid(InternalTypes.BYTE_ARRAY, gcConf.stackSize)
val stackMemory = new StackMemory(objRef, gcConf.stackSize)
stackMemory
}
}
......@@ -2,9 +2,16 @@ package uvm.refimpl.mem
import uvm.types._
import TypeSizes._
import com.typesafe.scalalogging.Logger
import org.slf4j.LoggerFactory
abstract class Mutator(implicit memorySupport: MemorySupport) {
object Mutator {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
}
abstract class Mutator(implicit memorySupport: MemorySupport) {
import Mutator._
def alloc(size: Word, align: Word, headerSize: Word): Word
def newScalar(ty: Type): Word = {
......@@ -13,6 +20,7 @@ abstract class Mutator(implicit memorySupport: MemorySupport) {
val align = alignOf(ty)
val objAddr = alloc(size, align, GC_HEADER_SIZE_SCALAR)
HeaderUtils.postAllocScalar(objAddr, tag)
logger.debug("newScalar: objAddr=%d 0x%x, ty=%s".format(objAddr, objAddr, ty))
objAddr
}
......@@ -22,6 +30,7 @@ abstract class Mutator(implicit memorySupport: MemorySupport) {
val align = hybridAlignOf(ty, len)
val objAddr = alloc(size, align, GC_HEADER_SIZE_HYBRID)
HeaderUtils.postAllocHybrid(objAddr, tag, len)
logger.debug("newHybrid: objAddr=%d 0x%x, len=%d 0x%x, ty=%s".format(objAddr, objAddr, len, len, ty))
objAddr
}
......@@ -31,6 +40,7 @@ abstract class Mutator(implicit memorySupport: MemorySupport) {
val align = alignOf(ty)
val objAddr = sm.alloc(size, align, GC_HEADER_SIZE_SCALAR)
HeaderUtils.postAllocScalar(objAddr, tag)
logger.debug("allocaScalar: objAddr=%d 0x%x, ty=%s".format(objAddr, objAddr, ty))
objAddr
}
......@@ -40,6 +50,7 @@ abstract class Mutator(implicit memorySupport: MemorySupport) {
val align = hybridAlignOf(ty, len)
val objAddr = sm.alloc(size, align, GC_HEADER_SIZE_HYBRID)
HeaderUtils.postAllocHybrid(objAddr, tag, len)
logger.debug("allocaHybrid: objAddr=%d 0x%x, len=%d 0x%x, ty=%s".format(objAddr, objAddr, len, len, ty))
objAddr
}
......
package uvm.refimpl.mem.simpleimmix
import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.Logger
import uvm.refimpl._
import uvm.refimpl.mem._
import uvm.refimpl.mem.TypeSizes._
import uvm.refimpl.mem.los.LargeObjectSpace
import TypeSizes._
class SimpleImmixHeap(val begin: Word, val size: Word)(
object SimpleImmixHeap {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
}
class SimpleImmixHeap(val begin: Word, val sosSize: Word, val losSize: Word)(
implicit microVM: MicroVM, memorySupport: MemorySupport)
extends Heap {
import SimpleImmixHeap._
val mid = begin + size / 2
require(begin % 4096L == 0, "Heap beginning must be 4096 bytes aligned. actual beginning: %d".format(begin))
val losBegin = begin + sosSize
val losEnd = losBegin + losSize
logger.debug("Small object space: %d 0x%x to %d 0x%x".format(begin, begin, losBegin, losBegin))
logger.debug("Large object space: %d 0x%x to %d 0x%x".format(losBegin, losBegin, losEnd, losEnd))
val space: SimpleImmixSpace = new SimpleImmixSpace(this, "SimpleImmixSpace", begin, size / 2)
val space: SimpleImmixSpace = new SimpleImmixSpace(this, "SimpleImmixSpace", begin, sosSize)
val los: LargeObjectSpace = new LargeObjectSpace(this, "Large object space", mid, size / 2)
val los: LargeObjectSpace = new LargeObjectSpace(this, "Large object space", losBegin, losSize)
val collector: SimpleImmixCollector = new SimpleImmixCollector(this, space, los)
......
......@@ -6,7 +6,7 @@ import uvm.refimpl.HowToResume.PassValues
object FactorialFromRPython extends App {
import uvm.refimpl.RichMuCtx._
val microVM = new MicroVM()
val microVM = MicroVM()
val ctx = microVM.newContext()
......
......@@ -5,7 +5,7 @@ import uvm.refimpl._
object Interact extends App {
// Create the Mu instance
val microVM = new MicroVM() // #1
val microVM = MicroVM() // #1
// Implicitly convert names to IDs
implicit def idOf(name: String) = microVM.idOf(name) // #2
......
......@@ -59,7 +59,7 @@ abstract class UvmBundleTesterBase extends FlatSpec with Matchers {
}
}
def makeMicroVM(): MicroVM = new MicroVM()
def makeMicroVM(): MicroVM = MicroVM()
val microVM = makeMicroVM()
......
......@@ -23,7 +23,7 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
//"uvm.refimpl.mem.simpleimmix.SimpleImmixCollector$" -> DEBUG,
"uvm.refimpl.itpr" -> DEBUG)
override def makeMicroVM = new MicroVM(heapSize = 8L * 1024L * 1024L)
override def makeMicroVM = new MicroVM(new GCConf(sosSize = 4L * 1024L * 1024L, losSize = 4L * 1024L * 1024L))
preloadBundles("tests/uvm-refimpl-test/primitives.uir",
"tests/uvm-refimpl-test/basic-tests.uir")
......
......@@ -39,13 +39,13 @@ class UvmInterpreterStackGCTests extends UvmBundleTesterBase {
rv
}
// Half of the heap (1MB) space is LOS. It can accommodate 16 stacks.
override def makeMicroVM = new MicroVM(heapSize = 2L * 1024L * 1024L, stackSize = 63L * 1024L)
// With 1MiB space is LOS. It can accommodate 16 stacks.
override def makeMicroVM = new MicroVM(new GCConf(losSize = 1L * 1024L * 1024L, stackSize = 63L * 1024L))
"The memory manager" should "collect unreachable stacks." in {
val ctx = microVM.newContext()
val nStacks = ctx.handleFromInt64( 13)
val nStacks = ctx.handleFromInt64(13)
val func = ctx.handleFromFunc("@stackcollecttest")
testFunc(ctx, func, Seq(nStacks)) { (ctx, th, st, wp) =>
......
......@@ -20,7 +20,7 @@ class ObjectPinningTest extends UvmBundleTesterBase {
"uvm.refimpl.mem" -> INFO,
"uvm.refimpl.mem.simpleimmix.SimpleImmixCollector$" -> DEBUG)
override def makeMicroVM() = new MicroVM(heapSize = 512L * 1024L)
override def makeMicroVM() = new MicroVM(new GCConf(sosSize = 256L*1024L, losSize = 256L * 1024L))
def gc() = microVM.memoryManager.heap.mutatorTriggerAndWaitForGCEnd(false)
......
......@@ -15,7 +15,7 @@ class UvmMemOperationsSpec extends UvmBundleTesterBase {
// The heap size is intentionally reduced to make GC more often
// The heap is divided in two halves. There is a 256KiB small object space (with 8 32KiB blocks) and a 256KiB large
// object space.
override def makeMicroVM() = new MicroVM(heapSize = 512L * 1024L)
override def makeMicroVM() = new MicroVM(new GCConf(sosSize = 256L*1024L, losSize = 256L * 1024L))
microVM.memoryManager.heap.space.debugLogBlockStates()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment