Out of memory tricks?

Posted on
  • I'm trying to load the BMP085 module onto an esp8266 with 800 JSvars and I'm getting an our of memory error when the IDE loads the module:

    ERROR: Out of Memory!
    WARNING: Unable to create string as not enough memory
    

    I have 760 free JSvars after a reset:

    >reset
    =function () { [native code] }
    >console.log("Mem:", process.memory());
    Mem: { "free": 760, "usage": 40, "total": 800, "history": 26 }
    

    Does it make sense that this is not enough for this small module? Are there any tricks I could employ?

  • At this moment I do not have an Espruino board at hand to make a comparison of free space after reset and after requiring the module. I assume the modulle that is pulled from the Web is the minified version... http://www.espruino.com/modules/BMP085.m­in.js.

    I pulled the code, did some formatting, and you might shrink it a bit more by dropping some of the debug stuff... also some shortening of member (value/function property) names can help.

    function d(a){
    	this.i2c=a;
    	this.BME280_ADDRESS=118;
    	this.debug=!1;
    	96==this.read8(208)&&this.debug&&console­.log("This chip is BME280");
    	this.writeReg(242,1);
    	this.writeReg(244,39);
    	this.writeReg(245,160);
    	this.readCoefficients()
    }
    d.prototype.concatU8=function(a,b){
    	var c=new Uint8Array(a.length+b.length);
    	c.set(a);
    	c.set(b,a.length);
    	return c
    };
    d.prototype.convS16=function(a,b){
    	var c=(a<<8)+b;
    	c&32768&&(c=-(c-1^65535));
    	return c
    };
    d.prototype.writeReg=function(a,b){
    	this.i2c.writeTo(this.BME280_ADDRESS,[a,­b])
    };
    d.prototype.read8=function(a){
    	this.i2c.writeTo(this.BME280_ADDRESS,a);­
    	return this.i2c.readFrom(this.BME280_ADDRESS,1)­[0]
    };
    d.prototype.readCoefficients=function(){­
    	this.i2c.writeTo(this.BME280_ADDRESS,136­);
    	var a=this.i2c.readFrom(this.BME280_ADDRESS,­24);
    	this.i2c.writeTo(this.BME280_ADDRESS,161­);
    	a=this.concatU8(a,this.i2c.readFrom(this­.BME280_ADDRESS,1));
    	this.i2c.writeTo(this.BME280_ADDRESS,225­);
    	a=this.concatU8(a,this.i2c.readFrom(this­.BME280_ADDRESS,7));
    	this.dig_T1=a[1]<<8|a[0];
    	this.dig_T2=this.convS16(a[3],a[2]);
    	this.dig_T3=this.convS16(a[5],a[4]);
    	this.dig_P1=a[7]<<8|a[6];
    	this.dig_P2=this.convS16(a[9],a[8]);
    	this.dig_P3=this.convS16(a[11],a[10]);
    	this.dig_P4=this.convS16(a[13],a[12]);
    	this.dig_P5=this.convS16(a[15],a[14]);
    	this.dig_P6=this.convS16(a[17],a[16]);
    	this.dig_P7=this.convS16(a[19],a[18]);
    	this.dig_P8=this.convS16(a[21],a[20]);
    	this.dig_P9=this.convS16(a[23],a[22]);
    	this.dig_H1=a[24];
    	this.dig_H2=this.convS16(a[26],a[25]);
    	this.dig_H3=a[27];
    	this.dig_H4=a[28]<<4|15&a[29];
    	this.dig_H5=a[30]<<4|a[29]>>4&15;
    	this.dig_H6=a[31]
    };
    d.prototype.readRawData=function(){this.­i2c.writeTo(this.BME280_ADDRESS,247);
    	var a=this.i2c.readFrom(this.BME280_ADDRESS,­8);
    	this.debug&&(console.log("d0: "+a[0]),console.log("d1: "+a[1])
    		,console.log("d2: "+a[2]),console.log("d3: "+a[3])
    		,console.log("d4: "+a[4]),console.log("d5: "+a[5])
    		,console.log("d6: "+a[6]),console.log("d7: "+a[7]));
    	this.pres_raw=a[0]<<12|a[1]<<4|a[2]>>4;
    	this.temp_raw=a[3]<<12|a[4]<<4|a[5]>>4;
    	this.hum_raw=a[6]<<8|a[7]
    };
    d.prototype.calibration_T=function(a){
    	var b;
    	b=(a/16384-this.dig_T1/1024)*this.dig_T2­;
    	a=(a/131072-this.dig_T1/8192)*(a/131072-­this.dig_T1/8192)*this.dig_T3;
    	this.t_fine=b+a;
    	return(b+a)/5120*100
    };
    d.prototype.calibration_P=function(a){
    	this.debug&&(console.log("T1: "+this.dig_T1),console.log("T2: "+this.dig_T2)
    		,console.log("T3: "+this.dig_T3),console.log("H1: "+this.dig_H1)
    		,console.log("H2: "+this.dig_H2),console.log("H3: "+this.dig_H3)
    		,console.log("H4: "+this.dig_H4),console.log("H5: "+this.dig_H5)
    		,console.log("H6: "+this.dig_H6),console.log("P1: "+this.dig_P1)
    		,console.log("P2: "+this.dig_P2),console.log("P3: "+this.dig_P3)
    		,console.log("P4: "+this.dig_P4),console.log("P5: "+this.dig_P5)
    		,console.log("P6: "+this.dig_P6),console.log("P7: "+this.dig_P7)
    		,console.log("P8: "+this.dig_P8),console.log("P9: "+this.dig_P9));
    	var b,c;
    	b=this.t_fine/2-64E3;
    	c=b*b*this.dig_P6/32768;
    	c+=b*this.dig_P5*2;
    	c=c/4+65536*this.dig_P4;
    	b=(this.dig_P3*b*b/524288+this.dig_P2*b)­/524288;
    	b=(1+b/32768)*this.dig_P1;
    	if(0===b)return 0;
    	a=6250*(1048576-a-c/4096)/b;
    	b=this.dig_P9*a*a/2147483648;
    	c=a*this.dig_P8/32768;
    	return a+=(b+c+this.dig_P7)/16
    };
    d.prototype.calibration_H=function(a){va­r b;
    	b=this.t_fine-76800;
    	b=((a<<14)-(this.dig_H4<<20)-this.dig_H5­*b+16384>>15)
    		*((((b*this.dig_H6>>10)*((b*this.dig_H3>­>11)+32768)>>10)+2097152)*this.dig_H2+81­92>>14);
    	b-=((b>>15)*(b>>15)>>7)*this.dig_H1>>4;
    	b=0>b?0:b;
    	return(419430400<b?419430400:b)>>12
    };
    exports.connect=function(a){return new d(a)}
    

    Here is what I manually did to it:

    var d=function(a){
    	this.i2c=a;
    	this.addr=118;
    	this.wR(242,1);
    	this.wR(244,39);
    	this.wR(245,160);
    	this.rCs()
    },p=d.prototype;
    p.ccU8=function(a,b){
    	var c=new Uint8Array(a.length+b.length);
    	c.set(a);
    	c.set(b,a.length);
    	return c
    };
    p.cS16=function(a,b){
    	var c=(a<<8)+b;
    	c&32768&&(c=-(c-1^65535));
    	return c
    };
    	p.wR=function(a,b){
    	this.i2c.writeTo(this.addr,[a,b])
    };
    	p.r8=function(a){
    	this.i2c.writeTo(this.addr,a);
    	return this.i2c.readFrom(this.addr,1)[0]
    };
    p.rCs=function(){
    	this.i2c.writeTo(this.addr,136);
    	var a=this.i2c.readFrom(this.addr,24);
    	this.i2c.writeTo(this.addr,161);
    	a=this.ccU8(a,this.i2c.readFrom(this.add­r,1));
    	this.i2c.writeTo(this.addr,225);
    	a=this.ccU8(a,this.i2c.readFrom(this.add­r,7));
    	this.dT1=a[1]<<8|a[0];
    	this.dT2=this.cS16(a[3],a[2]);
    	this.dT3=this.cS16(a[5],a[4]);
    	this.dP1=a[7]<<8|a[6];
    	this.dP2=this.cS16(a[9],a[8]);
    	this.dP3=this.cS16(a[11],a[10]);
    	this.dP4=this.cS16(a[13],a[12]);
    	this.dP5=this.cS16(a[15],a[14]);
    	this.dP6=this.cS16(a[17],a[16]);
    	this.dP7=this.cS16(a[19],a[18]);
    	this.dP8=this.cS16(a[21],a[20]);
    	this.dP9=this.cS16(a[23],a[22]);
    	this.dH1=a[24];
    	this.dH2=this.cS16(a[26],a[25]);
    	this.dH3=a[27];
    	this.dH4=a[28]<<4|15&a[29];
    	this.dH5=a[30]<<4|a[29]>>4&15;
    	this.dH6=a[31]
    };
    	p.rRaw=function(){
    	this.i2c.writeTo(this.addr,247);
    	var a=this.i2c.readFrom(this.addr,8);
    	this.pres_raw=a[0]<<12|a[1]<<4|a[2]>>4;
    	this.temp_raw=a[3]<<12|a[4]<<4|a[5]>>4;
    	this.hum_raw=a[6]<<8|a[7]
    };
    	p.calT=function(a){
    	var b=(a/16384-this.dT1/1024)*this.dT2;
    	a=(a/131072-this.dT1/8192)*(a/131072-thi­s.dT1/8192)*this.dT3;
    	this.t_fine=b+a;
    	return(b+a)/5120*100
    };
    	p.calP=function(a){
    	var b=this.t_fine/2-64E3,c=b*b*this.dP6/3276­8;
    	c+=b*this.dP5*2;
    	c=c/4+65536*this.dP4;
    	b=(this.dP3*b*b/524288+this.dP2*b)/52428­8;
    	b=(1+b/32768)*this.dP1;
    	if(0===b)return 0;
    	a=6250*(1048576-a-c/4096)/b;
    	b=this.dP9*a*a/2147483648;
    	c=a*this.dP8/32768;
    	return a+=(b+c+this.dP7)/16
    };
    p.calH=function(a){
    	var b=this.t_fine-76800;
    	b=((a<<14)-(this.dH4<<20)-this.dH5*b+163­84>>15)
    		*((((b*this.dH6>>10)*((b*this.dH3>>11)+3­2768)>>10)+2097152)*this.dH2+8192>>14);
    	b-=((b>>15)*(b>>15)>>7)*this.dH1>>4;
    	b=0>b?0:b;
    	return(419430400<b?419430400:b)>>12
    };
    exports.connect=function(a){return new d(a)}
    

    And the attached file is the above code with tabs and new lines removed. I hope I did not mess with essentials... of course, it is a 'different' module... but constraints justify many means...

    ...reduction by 1374 bytes down to 2255 bytes... yes, looking at the code, it creates quite a lot of things...


    1 Attachment

  • It gets minified. After loading it "only" uses ~420 JSvars, but during loading the string itself makes it all blow up.
    The upload is:

    Modules.addCached("BMP085","function c(b,a){this.i2c=b;this.oss=\"undefined\"­!==typeof a?a:3;if(3<this.oss||0>this.oss)this.oss­=3;var d=this.read8(208);if(85!=d)return console.log(\"Bad ID of: \"+d),null;this.readCoefficients()}c.pro­totype.read16=function(b){this.i2c.write­To(119,b);b=this.i2c.readFrom(119,2);ret­urn b[0]<<8|b[1]};c.prototype.readS16=functi­on(b){this.i2c.writeTo(119,b);b=this.i2c­.readFrom(119,2);b=b[0]<<8|b[1];return 32767<=b?b-65536:b};c.prototype.read8=fu­nction(b){this.i2c.writeTo(119,b);return­ this.i2c.readFrom(119,\n1)[0]};c.prototy­pe.readCoefficients=function(){this.ac1=­this.readS16(170);this.ac2=this.readS16(­172);this.ac3=this.readS16(174);this.ac4­=this.read16(176);this.ac5=this.read16(1­78);this.ac6=this.read16(180);this.b1=th­is.readS16(182);this.b2=this.readS16(184­);this.mb=this.readS16(186);this.mc=this­.readS16(188);this.md=this.readS16(190)}­;c.prototype.readRawTemperature=function­(b){this.i2c.writeTo(119,[244,46]);var a=this;setTimeout(function(){b(a.read16(­246))},5)};c.prototype.readRawPressure=f­unction(b){this.i2c.writeTo(119,\n[244,5­2+(this.oss<<6)]);var a,d=this;switch(this.oss){case 0:a=5;break;case 1:a=8;break;case 2:a=14;break;case 3:a=26}setTimeout(function(){var a=d.read8(246),c=d.read8(247),e=d.read8(­248);b((a<<16)+(c<<8)+e>>8-d.oss)},a)};c­.prototype.getTemperature=function(b){va­r a=this;this.readRawTemperature(function(­d){d=Math.round((d-a.ac6)*a.ac5/32768);v­ar c=Math.round(2048*a.mc/(d+a.md));b((d+c+­8)/160)})};c.prototype.getPressure=funct­ion(b){var a=this;this.readRawTemperature(function(­c){a.readRawPressure(function(f){var g=\nMath.round((c-a.ac6)*a.ac5/32768),g=­g+Math.round(2048*a.mc/(g+a.md)),e=g-4E3­;f=Math.round((f-((4*a.ac1+(a.b2*(e*e>>1­2)>>11)+(a.ac2*e>>11)<<a.oss)+2>>2))/(a.­ac4*(((a.ac3*e>>13)+(a.b1*(e*e>>12)>>16)­+2>>2)+32768)>>15)*(5E4>>a.oss))<<1;b({p­ressure:f+(((f>>8)*(f>>8)*3038>>16)+(-73­57*f>>16)+3791>>4),temperature:(g+8)/160­})})})};c.prototype.getAltitude=function­(b,a){return 44330*(1-Math.pow(b/a,1/5.255))};c.proto­type.getSeaLevel=function(b,a){return b/Math.pow(1-a/44330,5.255)};exports.con­nect=function(b,\na){return new c(b,a)}");
    
  • I was not aware of the fact that the debug stuff is dropped automatically... so the savings are not that much as I calculated. I'm also not sure if we are talking about exactly the same module, because there is some code I cannot find in.... NEVER MIND... pulled something else to work on than BMP085... ~~~{:(

  • BMP085.min.js formatted:

    function c(b,a){
    	this.i2c=b;
    	this.oss="undefined"!==typeof a?a:3;
    	if(3<this.oss||0>this.oss)this.oss=3;
    	var d=this.read8(208);
    	if(85!=d)return console.log("Bad ID of: "+d),null;
    	this.readCoefficients()
    }
    c.prototype.read16=function(b){
    	this.i2c.writeTo(119,b);
    	b=this.i2c.readFrom(119,2);
    	return b[0]<<8|b[1]
    };
    c.prototype.readS16=function(b){
    	this.i2c.writeTo(119,b);
    	b=this.i2c.readFrom(119,2);
    	b=b[0]<<8|b[1];
    	return 32767<=b?b-65536:b
    };
    c.prototype.read8=function(b){
    	this.i2c.writeTo(119,b);
    	return this.i2c.readFrom(119,
    	1)[0]
    };
    c.prototype.readCoefficients=function(){­
    	this.ac1=this.readS16(170);
    	this.ac2=this.readS16(172);
    	this.ac3=this.readS16(174);
    	this.ac4=this.read16(176);
    	this.ac5=this.read16(178);
    	this.ac6=this.read16(180);
    	this.b1=this.readS16(182);
    	this.b2=this.readS16(184);
    	this.mb=this.readS16(186);
    	this.mc=this.readS16(188);
    	this.md=this.readS16(190)
    };
    c.prototype.readRawTemperature=function(­b){
    	this.i2c.writeTo(119,[244,46]);
    	var a=this;
    	setTimeout(function(){
    	b(a.read16(246))},5)
    };
    c.prototype.readRawPressure=function(b){­
    this.i2c.writeTo(119,[244,52+(this.oss<<­6)]);
    	var a,d=this;
    	switch(this.oss){
    		case 0:a=5;
    		break;
    		case 1:a=8;
    		break;
    		case 2:a=14;
    		break;
    		case 3:a=26
    	}
    	setTimeout(function(){
    			var a=d.read8(246),c=d.read8(247),e=d.read8(­248);
    			b((a<<16)+(c<<8)+e>>8-d.oss)
    		},a)
    };
    c.prototype.getTemperature=function(b){
    	var a=this;
    	this.readRawTemperature(function(d){
    		d=Math.round((d-a.ac6)*a.ac5/32768);
    		var c=Math.round(2048*a.mc/(d+a.md));
    		b((d+c+8)/160)})
    };
    c.prototype.getPressure=function(b){
    	var a=this;
    	this.readRawTemperature(function(c){
    		a.readRawPressure(function(f){
    			var g=Math.round((c-a.ac6)*a.ac5/32768),g=g+­Math.round(2048*a.mc/(g+a.md)),e=g-4E3;
    			f=Math.round((f-((4*a.ac1+(a.b2*(e*e>>12­)>>11)+(a.ac2*e>>11)<<a.oss)+2>>2))
    					/(a.ac4*(((a.ac3*e>>13)+(a.b1*(e*e>>12)>­>16)+2>>2)+32768)>>15)*(5E4>>a.oss))<<1;­
    			b({pressure:f+(((f>>8)*(f>>8)*3038>>16)+­(-7357*f>>16)+3791>>4),temperature:(g+8)­/160})
    		})
    	})
    };
    c.prototype.getAltitude=function(b,a){
    	return 44330*(1-Math.pow(b/a,1/5.255))
    };
    c.prototype.getSeaLevel=function(b,a){
    	return b/Math.pow(1-a/44330,5.255)
    };
    exports.connect=function(b,a){return new c(b,a)}
    

    There is not much you can do about...

    I just wonder what the nested inline runtime function creation does to memory consumption...

  • Hmm - interesting that the initial string itself makes it blow up.

    No sure what to suggest - I imagine that the 12 byte JsVars mean it's not quite as efficient at storing data in the normal, extendable String format

  • Do the 12-byte JSvars not support the extendable string format?

  • Oh, they do - but as each var needs a pointer and flags, the efficiency goes down.

    • On 16 byte vars, you've got 12 bytes of data in a StringExt
    • On 12 byte vars, you've only got 8 bytes of data

    Potentially the extra 2 bits for lastChild could maybe go into flags saving you 1 byte and letting you use 9/12 bytes for a StringExt, but it's not huge.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Out of memory tricks?

Posted by Avatar for tve @tve

Actions