Skip to content

Commit

Permalink
fix(swingset): raise meter FULL from 1e7 to 1e8
Browse files Browse the repository at this point in the history
The agoric-cli/integration-test was failing: while dapp-encouragement's
contract was being installed into the on-chain Zoe, the zoe.install() crank
spends a lot of time in babel applying the metering transform to the contract
bundle. Most of this happens during import, after the initial nestedEvaluate
wrapper executes, which means the global meter is active. All that babel code
calls primordial functions like Object.keys a lot, and all those calls count
against the meter. The dapp-encouragement contract required about 12M total
units, and the default meter budget only allowed 10M.

After raising the meter value to let the test pass, @michaelfig helped me
find a way to turn off the global meter while inside the transform functions,
so we don't "charge" contracts for their usage of Babel. This reduces the
dapp-encouragement evaluation's metering usage by at least a factor of 10,
making the budget increase mostly unnecessary. I'm going to leave the
framework for it in place for now, however. We might lower the budget back to
the original values later, if meter-exhausting tests take too long, but so
far they seem to "finish" infinite loops quickly enough.
  • Loading branch information
warner committed Jun 26, 2020
1 parent c9fab80 commit deb2c16
Showing 1 changed file with 51 additions and 22 deletions.
73 changes: 51 additions & 22 deletions packages/SwingSet/src/kernel/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default function buildKernel(kernelEndowments) {
// It is important that tameMetering() was called by application startup,
// before install-ses. We expect the controller to run tameMetering() again
// (and rely upon its only-once behavior) to get the control facet
// (replaceGlobalMeter), and pass it in through kernelEndowments
// (replaceGlobalMeter), and pass it to us through kernelEndowments.

// TODO: be more clever. Only refill meters that were touched. Use a
// WeakMap. Drop meters that vats forget. Minimize the fast path. Then wrap
Expand All @@ -52,34 +52,45 @@ export default function buildKernel(kernelEndowments) {
let complainedAboutGlobalMetering = false;
const allRefillers = new Set(); // refill() functions
function makeGetMeter(options = {}) {
options = {
refillEachCrank: true,
refillIfExhausted: true,
...options,
};
const { meter, refillFacet } = makeMeter();
const { refillEachCrank = true, refillIfExhausted = true } = options;

if (!replaceGlobalMeter && !complainedAboutGlobalMetering) {
console.error(
`note: makeGetMeter() cannot enable global metering, app must import @agoric/install-metering-and-ses`,
);
// to fix this, your application program must
// `import('@agoric/install-metering-and-ses')` instead of
// `import('@agoric/install-ses')`
complainedAboutGlobalMetering = true;
}

// zoe's importBundle(dapp-encouragement contract) causes babel to use
// about 5.4M computrons and 6.4M allocatrons (total 11.8M) in a single
// crank, so the default of 1e7 is insufficient
const FULL = 1e8;
const { meter, refillFacet } = makeMeter({
budgetAllocate: FULL,
budgetCompute: FULL,
});
function refill() {
if (meter.isExhausted() && !options.refillIfExhausted) {
// console.error(`-- METER REFILL`);
// console.error(` allocate used: ${FULL - refillFacet.getAllocateBalance()}`);
// console.error(` compute used : ${FULL - refillFacet.getComputeBalance()}`);
if (meter.isExhausted() && !refillIfExhausted) {
return;
}
refillFacet.combined();
refillFacet.allocate();
refillFacet.compute();
refillFacet.stack();
}
if (options.refillEachCrank) {
if (refillEachCrank) {
allRefillers.add(refill);
}
let meterUsed = false; // mostly for testing
function getMeter() {
function getMeter(dontReplaceGlobalMeter = false) {
meterUsed = true;
if (replaceGlobalMeter) {
if (replaceGlobalMeter && !dontReplaceGlobalMeter) {
replaceGlobalMeter(meter);
} else if (!complainedAboutGlobalMetering) {
console.log(
`note: setMeter() cannot enable global metering, app must import @agoric/install-metering-and-ses`,
);
// to fix this, your application program must
// `import('@agoric/install-metering-and-ses')` instead of
// `import('@agoric/install-ses')`
complainedAboutGlobalMetering = true;
}
return meter;
}
Expand All @@ -92,6 +103,7 @@ export default function buildKernel(kernelEndowments) {
function isExhausted() {
return meter.isExhausted();
}

// TODO: this will evolve. For now, we let the caller refill the meter as
// much as they want, although only tests will do this (normal vats will
// just rely on refillEachCrank). As this matures, vats will only be able
Expand All @@ -103,6 +115,9 @@ export default function buildKernel(kernelEndowments) {
resetMeterUsed,
getMeterUsed,
refillFacet,
getAllocateBalance: refillFacet.getAllocateBalance,
getComputeBalance: refillFacet.getComputeBalance,
getCombinedBalance: refillFacet.getCombinedBalance,
});
}
harden(makeGetMeter);
Expand All @@ -117,6 +132,18 @@ export default function buildKernel(kernelEndowments) {
}
harden(endOfCrankMeterTask);

function runWithoutGlobalMeter(f, ...args) {
if (!replaceGlobalMeter) {
return f(...args);
}
const oldMeter = replaceGlobalMeter(null);
try {
return f(...args);
} finally {
replaceGlobalMeter(oldMeter);
}
}

let started = false;
// this holds externally-added vats, which are present at startup, but not
// vats that are added later from within the kernel
Expand Down Expand Up @@ -290,8 +317,10 @@ export default function buildKernel(kernelEndowments) {
Remotable,
getInterfaceOf,
makeGetMeter,
transformMetering,
transformTildot,
transformMetering: (...args) =>
runWithoutGlobalMeter(transformMetering, ...args),
transformTildot: (...args) =>
runWithoutGlobalMeter(transformTildot, ...args),
});

const syscallManager = harden({
Expand Down

0 comments on commit deb2c16

Please sign in to comment.