Skip to content

Commit bca74e1

Browse files
committed
Speed/docs/reliability fixes for recent defrag changes
1 parent b3f6bd6 commit bca74e1

File tree

3 files changed

+61
-17
lines changed

3 files changed

+61
-17
lines changed

src/jsvar.c

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4366,9 +4366,9 @@ int jsvGarbageCollect() {
43664366
}
43674367

43684368
#ifndef SAVE_ON_FLASH
4369-
static void _jsvDefragment_moveReferences(JsVarRef defragFromRef, JsVarRef defragToRef) {
4369+
static void _jsvDefragment_moveReferences(JsVarRef defragFromRef, JsVarRef defragToRef, unsigned int lastAllocated) {
43704370
// find references!
4371-
for (JsVarRef vr=1;vr<=jsVarsSize;vr++) {
4371+
for (JsVarRef vr=1;vr<=lastAllocated;vr++) {
43724372
JsVar *v = _jsvGetAddressOf(vr);
43734373
if ((v->flags&JSV_VARTYPEMASK)!=JSV_UNUSED) {
43744374
if (jsvIsFlatString(v)) {
@@ -4404,17 +4404,29 @@ void jsvDefragment() {
44044404
// garbage collect - removes cruft, also puts free list in order
44054405
jsvGarbageCollect();
44064406
jshInterruptOff();
4407+
const unsigned int minMove = 20; // don't move vars back less than this or we're just wasting CPU time
4408+
// find last allocated block of memory - speeds up searches!
4409+
unsigned int lastAllocated = 0;
4410+
for (JsVarRef i=1;i<=jsVarsSize;i++) {
4411+
JsVar *v = _jsvGetAddressOf(i);
4412+
if ((v->flags&JSV_VARTYPEMASK)!=JSV_UNUSED) {
4413+
if (jsvIsFlatString(v)) {
4414+
i += 1+(unsigned int)jsvGetFlatStringBlocks(v); // skip forward
4415+
}
4416+
lastAllocated = i;
4417+
}
4418+
}
44074419
// the var we're planning on writing to
44084420
JsVarRef defragToRef = 1;
44094421
// now for all blocks in memory...
4410-
for (JsVarRef defragFromRef=1;defragFromRef<=jsVarsSize;defragFromRef++) {
4422+
for (JsVarRef defragFromRef=1;defragFromRef<=lastAllocated;defragFromRef++) {
44114423
// First move our destination block on until we find an UNUSED one...
44124424
JsVar *defragTo = _jsvGetAddressOf(defragToRef);
44134425
while ((defragTo->flags&JSV_VARTYPEMASK)!=JSV_UNUSED) {
44144426
if (jsvIsFlatString(defragTo)) {
44154427
defragToRef += 1+(unsigned int)jsvGetFlatStringBlocks(defragTo); // skip forward
44164428
} else defragToRef++;
4417-
if (defragToRef > jsVarsSize) { // no more free blocks? quit
4429+
if (defragToRef > lastAllocated) { // no more free blocks? quit
44184430
jsvCreateEmptyVarList();
44194431
jshInterruptOn();
44204432
return;
@@ -4432,11 +4444,11 @@ void jsvDefragment() {
44324444
from defragToRef trying to see how many blocks we can move */
44334445
JsVarRef fsToRef = defragToRef;
44344446
bool isClear = false;
4435-
while (!isClear && fsToRef<defragFromRef) {
4447+
while (!isClear && (defragFromRef > fsToRef+minMove)) {
44364448
isClear = true;
44374449
// check area in fsToRef to see if it's clear
4438-
for (int i=0;i<blocksNeeded;i++) {
4439-
// TODO: what if we overlap with ourself?? would have to ensure we don't clear overlapping area
4450+
for (unsigned int i=0;i<blocksNeeded;i++) {
4451+
// TODO: what if we want to overlap with ourself?? would have to ensure we don't clear overlapping area
44404452
if ((_jsvGetAddressOf(fsToRef+i)->flags&JSV_VARTYPEMASK)!=JSV_UNUSED) {
44414453
isClear = false; // it's not clear!
44424454
fsToRef += i; // jump to this used block
@@ -4450,33 +4462,32 @@ void jsvDefragment() {
44504462
if (jsvIsFlatString(v)) {
44514463
fsToRef += 1+(unsigned int)jsvGetFlatStringBlocks(v); // skip forward
44524464
} else fsToRef++;
4453-
if (fsToRef <= jsVarsSize)
4454-
v = _jsvGetAddressOf(defragToRef);
4465+
if (fsToRef <= lastAllocated)
4466+
v = _jsvGetAddressOf(fsToRef);
44554467
else
44564468
break; // end of memory
44574469
}
44584470
}
44594471
}
4460-
if (isClear && (defragFromRef > fsToRef)) {
4472+
if (isClear && (defragFromRef > fsToRef+minMove)) { // can we move it earlier in memory?
44614473
//jsiConsolePrintf("Move FlatString %d -> %d\n", defragFromRef, fsToRef);
44624474
defragTo = _jsvGetAddressOf(fsToRef);
44634475
// copy data and clear old var
44644476
memmove(defragTo, defragFrom, sizeof(JsVar)*blocksNeeded);
44654477
memset(defragFrom, 0, sizeof(JsVar)*blocksNeeded);
44664478
// copy references
4467-
_jsvDefragment_moveReferences(defragFromRef, defragToRef);
4479+
_jsvDefragment_moveReferences(defragFromRef, fsToRef, lastAllocated);
44684480
}
44694481
}
44704482
defragFromRef += blocksNeeded-1; // skip forward (for loop adds 1)
4471-
// skip flat strings for now
44724483
} else if (canMove) { // moving a single var
4473-
if (defragFromRef > defragToRef) { // can we move back?
4484+
if (defragFromRef > defragToRef+minMove) { // can we move it earlier in memory?
44744485
//jsiConsolePrintf("Move JsVar %d -> %d\n", defragFromRef, defragToRef);
44754486
// copy data and clear old var
44764487
*defragTo = *defragFrom;
44774488
memset(defragFrom, 0, sizeof(JsVar)); // set flags to 0=unused
44784489
// copy references
4479-
_jsvDefragment_moveReferences(defragFromRef, defragToRef);
4490+
_jsvDefragment_moveReferences(defragFromRef, defragToRef, lastAllocated);
44804491
}
44814492
}
44824493
}

src/jswrap_espruino.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,16 +1563,25 @@ void jswrap_espruino_dumpFreeList() {
15631563
"ifndef" : "SAVE_ON_FLASH",
15641564
"generate" : "jswrap_e_dumpFragmentation"
15651565
}
1566-
Show fragmentation.
1566+
Show fragmentation. As of 2v27 this stops at the last allocated variable
1567+
so as to avoid outputting blank lines if memory isn't full.
15671568
15681569
* ` ` is free space
15691570
* `#` is a normal variable
15701571
* `L` is a locked variable (address used, cannot be moved)
15711572
* `=` represents data in a Flat String (must be contiguous)
15721573
*/
15731574
void jswrap_e_dumpFragmentation() {
1574-
int l = 0;
1575+
// find last allocated
1576+
unsigned int lastAllocated = 0;
15751577
for (unsigned int i=0;i<jsvGetMemoryTotal();i++) {
1578+
JsVar *v = _jsvGetAddressOf(i+1);
1579+
if ((v->flags&JSV_VARTYPEMASK)!=JSV_UNUSED)
1580+
lastAllocated = i;
1581+
}
1582+
// output data as lines
1583+
int l = 0;
1584+
for (unsigned int i=0;i<lastAllocated;i++) {
15761585
JsVar *v = _jsvGetAddressOf(i+1);
15771586
if ((v->flags&JSV_VARTYPEMASK)==JSV_UNUSED) {
15781587
jsiConsolePrint(" ");
@@ -1662,7 +1671,16 @@ void jswrap_e_dumpVariables() {
16621671
"name" : "defrag",
16631672
"generate" : "jsvDefragment"
16641673
}
1665-
BETA: defragment memory!
1674+
This defragment's Espruino's memory.
1675+
1676+
While Espruino does a lot of work to avoid fragmentation (variables spread over memory)
1677+
and can usually work around it (such as by allocating data in chunks) sometimes
1678+
it is useful to be able to allocate a large contiguous chunk of memory, and
1679+
if memory is low and has been fragmented it may need defragmenting in order to
1680+
find that chunk.
1681+
1682+
See `E.dumpFragmentation()` to show a map of the arrangement of variables
1683+
within memory.
16661684
*/
16671685

16681686
/*TYPESCRIPT

tests/test_defrag.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var a = [];
2+
for (var i=0;i<100;i++) {
3+
a[i]=E.toFlatString(i+"oh hello world a big flat string -------------------------------------------------------------------------------".substr(0,25+Math.randInt(50)));
4+
}
5+
for (var i=0;i<50;i++) { var n = Math.randInt(100); a[n]=n.toString(); }
6+
var pos = E.getAddressOf(a[99]);
7+
print("\nBefore:");
8+
E.dumpFragmentation();
9+
E.defrag();
10+
print("\nAfter:");
11+
E.dumpFragmentation();
12+
for (var i=0;i<100;i++) {
13+
if (!a[i].startsWith(i)) throw new Error("Entry "+i+" is wrong");
14+
}
15+
result = E.getAddressOf(a[99]) < pos; // it moved!

0 commit comments

Comments
 (0)