diff --git a/package.json b/package.json index 925bacaeb..7139179bf 100644 --- a/package.json +++ b/package.json @@ -64,8 +64,7 @@ "autocomplete": "node ./scripts/updateAutocompleteDocs.js", "build_sicp_package": "./scripts/build_sicp_package.sh", "publish_sicp_package": "./scripts/publish_sicp_package.sh", - "benchmark": "jest --runInBand --testPathPattern='.*benchmark.*' --testPathIgnorePatterns='/dist/'", - "prepare": "husky install" + "benchmark": "jest --runInBand --testPathPattern='.*benchmark.*' --testPathIgnorePatterns='/dist/'" }, "devDependencies": { "@types/jest": "^29.0.0", diff --git a/src/go-slang/ece.ts b/src/go-slang/ece.ts index 0f7cf2f05..9065373aa 100644 --- a/src/go-slang/ece.ts +++ b/src/go-slang/ece.ts @@ -9,7 +9,7 @@ import { PREDECLARED_FUNCTIONS, PREDECLARED_IDENTIFIERS } from './lib/predeclare import { Scheduler } from './scheduler' import { BuiltinOp, CallExpression, Instruction, NodeType, SourceFile } from './types' -export function evaluate(program: SourceFile, slangContext: SlangContext): Value { +export function evaluate(program: SourceFile, heapSize: number, slangContext: SlangContext): Value { const scheduler = new Scheduler(slangContext) const C = new Stack() @@ -18,7 +18,7 @@ export function evaluate(program: SourceFile, slangContext: SlangContext): Value // `SourceFile` is the root node of the AST which has latest (monotonically increasing) uid of all AST nodes // Therefore, the next uid to be used to track AST nodes is the uid of SourceFile + 1 const A = new AstMap((program.uid as number) + 1) - const H = new Heap(A, scheduler) + const H = new Heap(A, scheduler, heapSize) // inject predeclared functions into the global environment const B = new Map any>() diff --git a/src/go-slang/index.ts b/src/go-slang/index.ts index 7f7df719c..77b2bc3eb 100644 --- a/src/go-slang/index.ts +++ b/src/go-slang/index.ts @@ -3,8 +3,8 @@ import { resolvedErrorPromise } from '../runner' import { evaluate } from './ece' import { SourceFile } from './types' -export async function goRunner(program: any, context: Context): Promise { - const value = evaluate(program as SourceFile, context) +export async function goRunner(program: any, heapSize: number, context: Context): Promise { + const value = evaluate(program as SourceFile, heapSize, context) if (context.errors.length > 0) { return resolvedErrorPromise } diff --git a/src/go-slang/lib/heap/config.ts b/src/go-slang/lib/heap/config.ts index 9b3c8fe69..3117b51b3 100644 --- a/src/go-slang/lib/heap/config.ts +++ b/src/go-slang/lib/heap/config.ts @@ -1,12 +1,12 @@ /* Set of default configurations for the heap used in the Go ECE */ -// The default size of the heap in words -// Total heap size (in bytes) = DEFAULT_HEAP_SIZE * WORD_SIZE -export const DEFAULT_HEAP_SIZE = 4096 // in words - // The smallest addressable unit in the heap // We can think of it as the heap containing N number of words, each of size WORD_SIZE export const WORD_SIZE = 8 // in bytes +// The default size of the heap in words +// Total heap size (in bytes) +export const DEFAULT_HEAP_SIZE = 4096 * WORD_SIZE + // The byte offset to the size of a heap object within a tagged pointer export const SIZE_OFFSET = 5 // in bytes diff --git a/src/go-slang/lib/heap/index.ts b/src/go-slang/lib/heap/index.ts index 218ca4497..f64240507 100644 --- a/src/go-slang/lib/heap/index.ts +++ b/src/go-slang/lib/heap/index.ts @@ -52,14 +52,14 @@ export class Heap { // NOTE: this is hacky and should be refactored private scheduler: Scheduler - constructor(astMap: AstMap, scheduler: Scheduler, n_words?: number) { + constructor(astMap: AstMap, scheduler: Scheduler, heapSize: number = DEFAULT_HEAP_SIZE) { this.astMap = astMap this.scheduler = scheduler - const totalBytes = (n_words ?? DEFAULT_HEAP_SIZE) * WORD_SIZE - this.memory = new DataView(new ArrayBuffer(totalBytes)) + console.log(`[Heap]: Initializing heap with size ${heapSize} bytes.`) // DEBUG + this.memory = new DataView(new ArrayBuffer(heapSize)) - const buddyAllocSize = Math.ceil(Math.log2(totalBytes)) + const buddyAllocSize = Math.ceil(Math.log2(heapSize)) this.buddyBlocks = Array.from({ length: buddyAllocSize + 1 }, () => new Set()) // initialize the block with the maximum size (= size of the entire heap) @@ -382,11 +382,14 @@ export class Heap { } } + let _totalFreedWords = 0 // DEBUG this.buddyBlockMap.forEach((_, addr) => { if (!activeHeapAddresses.has(addr) && this.tag(addr) !== PointerTag.AstNode) { + _totalFreedWords += this.size(addr) + 1 this.buddyFree(addr) } }) + console.log(`[Heap]: GC freed ${_totalFreedWords * WORD_SIZE} bytes of memory.`) // DEBUG // retry allocation alloc_heap_addr = this.buddyAlloc((size + 1) * WORD_SIZE) diff --git a/src/index.ts b/src/index.ts index a590a1ec9..20345f196 100644 --- a/src/index.ts +++ b/src/index.ts @@ -50,6 +50,7 @@ export interface IOptions { executionMethod: ExecutionMethod variant: Variant originalMaxExecTime: number + heapSize: number useSubst: boolean isPrelude: boolean throwInfiniteLoops: boolean @@ -234,7 +235,8 @@ export async function runFilesInContext( if (program === null) { return resolvedErrorPromise } - return goRunner(program, context) + const { heapSize } = options + return goRunner(program, heapSize!, context) } if ( diff --git a/src/runner/sourceRunner.ts b/src/runner/sourceRunner.ts index ee6e6c085..17783e0d5 100644 --- a/src/runner/sourceRunner.ts +++ b/src/runner/sourceRunner.ts @@ -45,6 +45,7 @@ const DEFAULT_SOURCE_OPTIONS: Readonly = { executionMethod: 'auto', variant: Variant.DEFAULT, originalMaxExecTime: 1000, + heapSize: 32768, useSubst: false, isPrelude: false, throwInfiniteLoops: true,