working memory tracking utility

master
David Kebler 2020-08-01 07:54:34 -07:00
parent 7f65b75491
commit a259b09a7e
8 changed files with 251 additions and 0 deletions

37
.eslintrc.js Normal file
View File

@ -0,0 +1,37 @@
module.exports = {
"ecmaFeatures": {
"modules": true,
"spread" : true,
"restParams" : true
},
// "plugins": [
// "unicorn"
// ],
"env": {
"es6": true,
"node": true,
"mocha": true
},
"parserOptions": {
"ecmaVersion": 2017,
"sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
2
],
// "unicorn/no-array-instanceof": "error",
"no-console": 0,
"semi": ["error", "never"],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
]
}
}

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/node_modules/
/coverage/

5
.npmignore Normal file
View File

@ -0,0 +1,5 @@
tests/
test/
*.test.js
testing/
examples/

22
examples/example.js Normal file
View File

@ -0,0 +1,22 @@
import Memory from '../src/memory.js'
const mem = new Memory({db:0})
// let text
// mem.watch(console.log)
mem.watch()
setInterval(()=>
mem.emit('tick')
,5000)
mem.on('tick',()=> {
mem.log({msg:'testing'})
let text
for (let i = 0; i < 1000000; i++) {
text += 'a'
}
mem.text = text
console.log('memory that text is using', mem.sizeof(mem.text),mem.sizeof(text), 'M')
})

3
nodemon.json Normal file
View File

@ -0,0 +1,3 @@
{
"ignore":["examples/*.json"]
}

33
package.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "@uci-utils/memory",
"version": "0.1.4",
"description": "memory logger and watcher",
"main": "src/memory.js",
"scripts": {
"example": "node -r esm examples/example",
"example:dev": "./node_modules/.bin/nodemon -r esm examples/example"
},
"author": "David Kebler",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/uCOMmandIt/.git"
},
"keywords": [
"node.js"
],
"bugs": {
"url": "https://github.com/uCOMmandIt/uci-utils/issues"
},
"homepage": "https://github.com/uCOMmandIt/uci-utils#readme",
"dependencies": {
"debounce-fn": "^4.0.0",
"exact-math": "^2.2.0",
"gc-stats": "^1.4.0",
"object-sizeof": "^1.6.1"
},
"devDependencies": {
"esm": "^3.2.25",
"nodemon": "^2.0.4"
}
}

18
readme.md Normal file
View File

@ -0,0 +1,18 @@
# uCOMmandIt Reactive Class
A Great Class to Extend! It has built in support to make reactive any/all class properties and their nested leaves.
## Why?
To have reactivity similar to vue and react but in any backend object?
This allow a backend app to maintain app settings and allow "clients" to get updated values when the app changes state. Essentially will create a 'mini' state event bus.
## Features
Will both emit and allow subscriptions to any property 'path' that has been made reactive.
Allows custom registration of unlimited state change event hooks.
Allows additional rxjs operators for the pipe the observer for each property.
See Examples folder

131
src/memory.js Normal file
View File

@ -0,0 +1,131 @@
import EventEmitter from 'events'
import gc from 'gc-stats'
import _ from 'exact-math'
import dbfn from 'debounce-fn'
import sizeof from 'object-sizeof'
import { getHeapStatistics } from 'v8'
const STATS =
{
total_heap_size: 'byte',
total_heap_size_executable: 'byte',
total_physical_size: 'byte',
total_available_size: 'byte',
used_heap_size: 'byte',
heap_size_limit: 'byte',
malloced_memory: 'byte',
peak_malloced_memory: 'byte',
does_zap_garbage: 'boolean',
number_of_native_contexts: 'val',
number_of_detached_contexts: 'val'
}
const PROPS = Object.keys(STATS)
class Memory extends EventEmitter {
constructor(opts={}) {
super()
this._props = Object.keys(this.stats)
this._diff = {}
PROPS.forEach(prop=>this._diff[prop]={})
this._current = this.get()
this.db = opts.db || 3000
this.reset()
this.last = this.first
this.gc = gc()
}
get stats () {
return getHeapStatistics()
}
sizeof (obj, u='M') {
return mb(sizeof(obj))
}
reset() {
this.first=this.get()
}
get() {
const mem = this.stats
PROPS.forEach(prop => {
if (STATS[prop] === 'byte') mem[prop] = mb(mem[prop],2)
})
return mem
}
snapshot() {
this.last=Object.assign({},this._current)
this._current=this.get()
this.remaining = _.sub(this._current.heap_size_limit,this._current.used_heap_size)
PROPS.forEach(prop => {
if (STATS[prop] !== 'boolean') {
this._diff[prop].last = _.sub(this._current[prop],this.last[prop])
this._diff[prop].overall=_.sub(this._current[prop],this.first[prop])
}
})
return { remaining:this.remaining, first:this.first, last:this.last, current:this._current, diff:this._diff }
}
log (opts) {
this.snapshot()
console.log(`============= ${opts.msg||''} =====================`,new Date().toLocaleString())
console.log(`remaining Heap: ${this.remaining} mb`)
console.log(this._log('used_heap_size'))
console.log(this._log('number_of_native_contexts'))
console.log(`============= ${opts.msg||''} =====================`)
}
_log (prop) {
return `${prop} > \
diff:${this._diff[prop].overall}, \
lastdiff:${this._diff[prop].last}, \
current:${this._current[prop]}, \
last:${this.last[prop]}, \
first:${this.first[prop]}, \
`
}
// totalHeapSize: 17911808,
// totalHeapExecutableSize: 573440,
// usedHeapSize: 13410032,
// heapSizeLimit: 2197815296,
// totalPhysicalSize: 17675040,
// totalAvailableSize: 2184093576,
// mallocedMemory: 8192,
// peakMallocedMemory: 897248,
// numberOfNativeContexts: 3,
// numberOfDetachedContexts: 0
watch (handler) {
gc().on('stats', dbfn(
stats => {
const diff = _.sub(stats.after.heapSizeLimit,stats.after.usedHeapSize)
if (stats.gctype > 0) {
if (!handler) {
console.log(stats.gctype,'----------- Heap at GC ------------',new Date().toLocaleString())
console.log(`Heap> \
remaining: ${mb(diff)} mb \
before:${mb(stats.before.usedHeapSize)}, \
after:${mb(stats.after.usedHeapSize)} mb, \
diff:${mb(stats.diff.usedHeapSize)} mb \
`)
console.log('-----------------------')
}
else handler(stats)
}},
{wait:this.db}
)
)}
} // end class
function mb (val,p=2) {
// console.log(val,val / 1024 / 1024, _.round(val / 1024 / 1024, -p) )
return _.round(val / 1024 / 1024, -p)
}
export default Memory
export { Memory }