Created
February 7, 2021 05:26
-
-
Save bgoonz/c15f693f54732d0090fa9d823890b34a to your computer and use it in GitHub Desktop.
json-jupyter.ipynb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"# Exploring Custom Revival with JSON.parse" | |
], | |
"metadata": {} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"$$svg$$ = `\n", | |
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 630 630\" height=\"90\">\n", | |
"<g id=\"logo\">\n", | |
" <rect id=\"background\" x=\"0\" y=\"0\" width=\"630\" height=\"630\" fill=\"#f7df1e\" />\n", | |
" <path id=\"j\" d=\"m 165.65,526.47375 48.2125,-29.1775 C 223.16375,513.7875 231.625,527.74 251.92,527.74 c 19.45375,0 31.71875,-7.60975 31.71875,-37.21 l 0,-201.3 59.20375,0 0,202.1375 c 0,61.32 -35.94375,89.23125 -88.385,89.23125 -47.36125,0 -74.8525,-24.52875 -88.8075,-54.13\" />\n", | |
" <path id=\"s\" d=\"m 375,520.13 48.20625,-27.91125 c 12.69,20.72375 29.1825,35.9475 58.36125,35.9475 24.53125,0 40.17375,-12.26475 40.17375,-29.18125 0,-20.29875 -16.06875,-27.48875 -43.135,-39.32625 l -14.7975,-6.3475 c -42.715,-18.18125 -71.05,-41.0175 -71.05,-89.2275 0,-44.40375 33.83125,-78.2375 86.695,-78.2375 37.6375,0 64.7025,13.11125 84.15375,47.36625 l -46.09625,29.60125 c -10.15,-18.1825 -21.1425,-25.37125 -38.0575,-25.37125 -17.33875,0 -28.335,10.995 -28.335,25.37125 0,17.7625 10.99625,24.9525 36.3675,35.94875 l 14.8,6.3425 c 50.325,21.56875 78.66,43.5575 78.66,93.03375 0,53.2875 -41.86625,82.465 -98.11,82.465 -54.97625,0 -90.5,-26.2175 -107.83625,-60.47375\" />\n", | |
"</g>\n", | |
"</svg>\n", | |
"`" | |
], | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 2, | |
"data": { | |
"image/svg+xml": "\n<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 630 630\" height=\"90\">\n<g id=\"logo\">\n <rect id=\"background\" x=\"0\" y=\"0\" width=\"630\" height=\"630\" fill=\"#f7df1e\" />\n <path id=\"j\" d=\"m 165.65,526.47375 48.2125,-29.1775 C 223.16375,513.7875 231.625,527.74 251.92,527.74 c 19.45375,0 31.71875,-7.60975 31.71875,-37.21 l 0,-201.3 59.20375,0 0,202.1375 c 0,61.32 -35.94375,89.23125 -88.385,89.23125 -47.36125,0 -74.8525,-24.52875 -88.8075,-54.13\" />\n <path id=\"s\" d=\"m 375,520.13 48.20625,-27.91125 c 12.69,20.72375 29.1825,35.9475 58.36125,35.9475 24.53125,0 40.17375,-12.26475 40.17375,-29.18125 0,-20.29875 -16.06875,-27.48875 -43.135,-39.32625 l -14.7975,-6.3475 c -42.715,-18.18125 -71.05,-41.0175 -71.05,-89.2275 0,-44.40375 33.83125,-78.2375 86.695,-78.2375 37.6375,0 64.7025,13.11125 84.15375,47.36625 l -46.09625,29.60125 c -10.15,-18.1825 -21.1425,-25.37125 -38.0575,-25.37125 -17.33875,0 -28.335,10.995 -28.335,25.37125 0,17.7625 10.99625,24.9525 36.3675,35.94875 l 14.8,6.3425 c 50.325,21.56875 78.66,43.5575 78.66,93.03375 0,53.2875 -41.86625,82.465 -98.11,82.465 -54.97625,0 -90.5,-26.2175 -107.83625,-60.47375\" />\n</g>\n</svg>\n" | |
}, | |
"metadata": {} | |
} | |
], | |
"execution_count": 2, | |
"metadata": { | |
"collapsed": false, | |
"outputHidden": false, | |
"inputHidden": true, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:02.522Z", | |
"iopub.status.busy": "2021-02-07T05:17:07.473Z", | |
"iopub.execute_input": "2021-02-07T05:17:07.606Z", | |
"iopub.status.idle": "2021-02-07T05:17:07.863Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"var Immutable = require('immutable')\n", | |
"var _ = require('lodash')" | |
], | |
"outputs": [], | |
"execution_count": 3, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:02.806Z", | |
"iopub.status.busy": "2021-02-07T05:17:08.198Z", | |
"iopub.execute_input": "2021-02-07T05:17:08.431Z", | |
"iopub.status.idle": "2021-02-07T05:17:08.699Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"# Revival on Parse\n", | |
"\n", | |
"JSON.parse takes an extra argument called a reviver:\n", | |
"\n", | |
"```\n", | |
"JSON.parse(text[, reviver])\n", | |
"```\n", | |
"\n", | |
"The reviver accepts two parameters, `key` and `value` and returns the intended `value`. The key will either be a text key on Objects or numbers for when the value is in an Array.\n", | |
"\n", | |
"Let's walk through some sample code to check this out." | |
], | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"// Classic JSON.parse\n", | |
"JSON.parse('{\"a\": 2, \"b\": { \"name\": \"dave\" }}')" | |
], | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 4, | |
"data": { | |
"text/plain": "{ a: 2, b: { name: 'dave' } }" | |
}, | |
"metadata": {} | |
} | |
], | |
"execution_count": 4, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:03.009Z", | |
"iopub.status.busy": "2021-02-07T05:17:08.831Z", | |
"iopub.execute_input": "2021-02-07T05:17:08.941Z", | |
"iopub.status.idle": "2021-02-07T05:17:09.090Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"function reviver(key, value) {\n", | |
" if(key === 'name') {\n", | |
" return value + \" senior\";\n", | |
" }\n", | |
" return value\n", | |
"}\n", | |
"\n", | |
"JSON.parse('{\"a\": 2, \"b\": { \"name\": \"dave\" }}', reviver)" | |
], | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 5, | |
"data": { | |
"text/plain": "{ a: 2, b: { name: 'dave senior' } }" | |
}, | |
"metadata": {} | |
} | |
], | |
"execution_count": 5, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:03.480Z", | |
"iopub.status.busy": "2021-02-07T05:17:09.366Z", | |
"iopub.execute_input": "2021-02-07T05:17:09.476Z", | |
"iopub.status.idle": "2021-02-07T05:17:09.751Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"This means you can use this to change values based on a key, though you won't know the nested path of the overall JSON object. \n", | |
"\n", | |
"Since the string is (expected to be) JSON, there are only two types which are not immutable: `Array` and `Object`. You can use this to your advantage to create frozen or Immutable.js objects while parsing." | |
], | |
"metadata": { | |
"outputExpanded": false | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"JSON.parse('{\"a\": 2, \"b\": { \"name\": \"dave\" }}', (k, v) => Object.freeze(v))" | |
], | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 6, | |
"data": { | |
"text/plain": "{ a: 2, b: { name: 'dave' } }" | |
}, | |
"metadata": {} | |
} | |
], | |
"execution_count": 6, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:03.758Z", | |
"iopub.status.busy": "2021-02-07T05:17:10.081Z", | |
"iopub.execute_input": "2021-02-07T05:17:10.305Z", | |
"iopub.status.idle": "2021-02-07T05:17:10.511Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"function immutableReviver(key, value) {\n", | |
" if (Array.isArray(value)) {\n", | |
" return Immutable.List(value);\n", | |
" }\n", | |
"\n", | |
" if (typeof value === 'object') {\n", | |
" return Immutable.Map(value)\n", | |
" }\n", | |
" return value;\n", | |
"}" | |
], | |
"outputs": [], | |
"execution_count": 7, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:03.888Z", | |
"iopub.status.busy": "2021-02-07T05:17:10.811Z", | |
"iopub.execute_input": "2021-02-07T05:17:10.959Z", | |
"iopub.status.idle": "2021-02-07T05:17:11.196Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"Since it seemed handy enough, I put [`immutable-reviver`](https://github.com/rgbkrk/immutable-reviver) on npm. We'll just use the version written here for now though." | |
], | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"revived = JSON.parse('{\"a\": 2, \"b\": { \"name\": \"dave\" }}', immutableReviver)" | |
], | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 8, | |
"data": { | |
"text/plain": "Map {\n size: 2,\n _root: ArrayMapNode { ownerID: OwnerID {}, entries: [ [Array], [Array] ] },\n __ownerID: undefined,\n __hash: undefined,\n __altered: false\n}" | |
}, | |
"metadata": {} | |
} | |
], | |
"execution_count": 8, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:04.149Z", | |
"iopub.status.busy": "2021-02-07T05:17:11.550Z", | |
"iopub.execute_input": "2021-02-07T05:17:11.734Z", | |
"iopub.status.idle": "2021-02-07T05:17:11.966Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"revived.getIn(['b', 'name'])" | |
], | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 9, | |
"data": { | |
"text/plain": "'dave'" | |
}, | |
"metadata": {} | |
} | |
], | |
"execution_count": 9, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:04.451Z", | |
"iopub.status.busy": "2021-02-07T05:17:12.227Z", | |
"iopub.execute_input": "2021-02-07T05:17:12.390Z", | |
"iopub.status.idle": "2021-02-07T05:17:12.789Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"The reason I started looking into this was because I was trying to see if I could optimize loading of notebooks in nteract. We currently rely on a strategy that goes like:\n", | |
"\n", | |
"```\n", | |
"notebook = JSON.parse(rawNotebook)\n", | |
"immutableNotebook = Immutable.fromJS(notebook)\n", | |
"\n", | |
"ourNotebook = immutableNotebook.map(...).map(...)... // A series of transformations to create our in-memory representation\n", | |
"```\n", | |
"\n", | |
"These transformations are mostly to turn notebook cells from this:\n", | |
"\n", | |
"\n", | |
"```\n", | |
"{\n", | |
" \"metadata\": {\n", | |
" \"collapsed\": false,\n", | |
" \"outputExpanded\": false\n", | |
" },\n", | |
" \"cell_type\": \"markdown\",\n", | |
" \"source\": [\n", | |
" \"# Outputs you can update by name\\n\",\n", | |
" \"\\n\",\n", | |
" \"This notebook demonstrates the new name-based display functionality in the notebook. Previously, notebooks could only attach output to the cell that was currently being executed:\\n\",\n", | |
" \"\\n\"\n", | |
" ]\n", | |
"}\n", | |
"```\n", | |
"\n", | |
"into:\n", | |
"\n", | |
"```\n", | |
"{\n", | |
" \"metadata\": {\n", | |
" \"collapsed\": false,\n", | |
" \"outputExpanded\": false\n", | |
" },\n", | |
" \"cell_type\": \"markdown\",\n", | |
" \"source\": \"# Outputs you can update by name\\n\\nThis notebook demonstrates the new name-based display functionality in the notebook. Previously, notebooks could only attach output to the cell that was currently being executed:\\n\\n\"\n", | |
"}\n", | |
"```\n", | |
"\n", | |
"This multi-line string format, introduced by Jupyter, is to accomodate diffing of notebooks in tools like git and GitHub. It's applied to source on cells as well as some output types." | |
], | |
"metadata": { | |
"outputExpanded": false | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"We can set up a reviver that handles all the keys that are most likely to have [multi-line strings](https://github.com/jupyter/nbformat/blob/62d6eb8803616d198eaa2024604d1fe923f2a7b3/nbformat/v4/nbformat.v4.schema.json#L386). We'll start with those that are media types that we know end up being encoded as an array of strings." | |
], | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"var multilineStringMimetypes = new Set([\n", | |
" 'application/javascript',\n", | |
" 'text/html',\n", | |
" 'text/markdown',\n", | |
" 'text/latex',\n", | |
" 'image/svg+xml',\n", | |
" 'image/gif',\n", | |
" 'image/png',\n", | |
" 'image/jpeg',\n", | |
" 'application/pdf',\n", | |
" 'text/plain',\n", | |
"]);\n", | |
"\n", | |
"function immutableNBReviver(key, value) {\n", | |
" if (Array.isArray(value)) {\n", | |
" if(multilineStringMimetypes.has(key)) {\n", | |
" return value.join('')\n", | |
" }\n", | |
" return Immutable.List(value);\n", | |
" }\n", | |
"\n", | |
" if (typeof value === 'object') {\n", | |
" return Immutable.Map(value)\n", | |
" }\n", | |
" return value;\n", | |
"}" | |
], | |
"outputs": [], | |
"execution_count": 10, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:04.766Z", | |
"iopub.status.busy": "2021-02-07T05:17:12.964Z", | |
"iopub.execute_input": "2021-02-07T05:17:13.036Z", | |
"iopub.status.idle": "2021-02-07T05:17:13.161Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"We can also set up a \"greedy\" reviver that will also convert `source` and `text` fields. The primary problem with this though, because of how JSON.parse works is that we have no idea if it's a key in a cell where we expect, part of someone else's JSON payload, or in metadata." | |
], | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"var specialKeys = new Set([\n", | |
" 'application/javascript',\n", | |
" 'text/html',\n", | |
" 'text/markdown',\n", | |
" 'text/latex',\n", | |
" 'image/svg+xml',\n", | |
" 'image/gif',\n", | |
" 'image/png',\n", | |
" 'image/jpeg',\n", | |
" 'application/pdf',\n", | |
" 'text/plain',\n", | |
" 'source',\n", | |
" 'text',\n", | |
"]);\n", | |
"\n", | |
"function immutableGreedyReviver(key, value) {\n", | |
" if (Array.isArray(value)) {\n", | |
" if(specialKeys.has(key)) {\n", | |
" return value.join('')\n", | |
" }\n", | |
" return Immutable.List(value);\n", | |
" }\n", | |
"\n", | |
" if (typeof value === 'object') {\n", | |
" return Immutable.Map(value)\n", | |
" }\n", | |
" return value;\n", | |
"}" | |
], | |
"outputs": [], | |
"execution_count": 11, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:05.091Z", | |
"iopub.status.busy": "2021-02-07T05:17:13.503Z", | |
"iopub.execute_input": "2021-02-07T05:17:13.851Z", | |
"iopub.status.idle": "2021-02-07T05:17:13.989Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"# Our runtime harnesses\n", | |
"\n", | |
"To evaluate the speed at which we can revive our objects, we'll set up a little testing harness." | |
], | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"// Some logger that uses process.hrtime that I ripped off Stack Overflow, since we want to use timing in a way that we can't with console.time\n", | |
"\n", | |
"[ a, o, ms, s, log ] = ( function * () {\n", | |
" yield * [\n", | |
" ( process.hrtime )(),\n", | |
" process.hrtime,\n", | |
" ms => ( ( ms[ 0 ] * 1e9 + ms[ 1 ] ) / 1000000 ),\n", | |
" s => s / 1000,\n", | |
" () => {\n", | |
" const f = o( a ), msf = ms( f ), sf = s( msf );\n", | |
" return { a, o: f, ms: msf, s: sf };\n", | |
" }\n", | |
" ];\n", | |
"} )();" | |
], | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 12, | |
"data": { | |
"text/plain": "Object [Generator] {}" | |
}, | |
"metadata": {} | |
} | |
], | |
"execution_count": 12, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:05.363Z", | |
"iopub.status.busy": "2021-02-07T05:17:14.181Z", | |
"iopub.execute_input": "2021-02-07T05:17:14.298Z", | |
"iopub.status.idle": "2021-02-07T05:17:14.514Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"// Calculate the milliseconds it takes to run f\n", | |
"function measure(f) {\n", | |
" start = log()\n", | |
" f()\n", | |
" end = log()\n", | |
" return end.ms - start.ms \n", | |
"}\n", | |
"\n", | |
"// measure the function run n times, return the mean\n", | |
"function runTrials(f, n=1000) {\n", | |
" values = []\n", | |
" for(var ii=0; ii < n; ii++) {\n", | |
" values.push(measure(f))\n", | |
" }\n", | |
" return values.reduce((a, b) => a + b, 0)/n\n", | |
"}" | |
], | |
"outputs": [], | |
"execution_count": 13, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:05.833Z", | |
"iopub.status.busy": "2021-02-07T05:17:14.742Z", | |
"iopub.execute_input": "2021-02-07T05:17:14.866Z", | |
"iopub.status.idle": "2021-02-07T05:17:15.007Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"With our harness all set up, we can run through all the notebooks we have locally to see how they perform with different revivers." | |
], | |
"metadata": { | |
"outputExpanded": false | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"notebooks = require('glob').sync('./*.ipynb')" | |
], | |
"outputs": [ | |
{ | |
"output_type": "error", | |
"ename": "Error", | |
"evalue": "Cannot find module 'glob'\nRequire stack:\n- C:\\ProgramData\\chocolatey\\lib\\nteract.portable\\tools\\nteract\\resources\\app.asar.unpacked\\node_modules\\@nteract\\examples\\node.js\\[eval]", | |
"traceback": [ | |
"internal/modules/cjs/loader.js:720", | |
" throw err;", | |
" ^", | |
"", | |
"Error: Cannot find module 'glob'", | |
"Require stack:", | |
"- C:\\ProgramData\\chocolatey\\lib\\nteract.portable\\tools\\nteract\\resources\\app.asar.unpacked\\node_modules\\@nteract\\examples\\node.js\\[eval]", | |
" at Function.Module._resolveFilename (internal/modules/cjs/loader.js:717:15)", | |
" at Module._load (internal/modules/cjs/loader.js:622:27)", | |
" at Module._load (electron/js2c/asar.js:717:26)", | |
" at Function.Module._load (electron/js2c/asar.js:717:26)", | |
" at Module.require (internal/modules/cjs/loader.js:775:19)", | |
" at require (internal/modules/cjs/helpers.js:68:18)", | |
" at evalmachine.<anonymous>:1:13", | |
" at Script.runInThisContext (vm.js:126:20)", | |
" at Object.runInThisContext (vm.js:316:38)", | |
" at run ([eval]:1054:15)" | |
] | |
} | |
], | |
"execution_count": 14, | |
"metadata": { | |
"collapsed": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:06.127Z", | |
"iopub.status.busy": "2021-02-07T05:17:15.183Z", | |
"iopub.execute_input": "2021-02-07T05:17:15.306Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"for(var notebookPath of notebooks) {\n", | |
" console.log(\"\\n ----- \", path.basename(notebookPath))\n", | |
" raw = fs.readFileSync(notebookPath)\n", | |
" \n", | |
" var tests = [\n", | |
" { name: 'straight JSON.parse', f: () => { JSON.parse(raw) } },\n", | |
" { name: 'Object.freeze', f: () => { JSON.parse(raw, (k, v) => Object.freeze(v)) } },\n", | |
" { name: 'basic Immutable', f: () => { JSON.parse(raw, immutableReviver) } },\n", | |
" { name: 'immutable notebook', f: () => { JSON.parse(raw, immutableNBReviver) } },\n", | |
" { name: 'immutable greedy nb', f: () => { JSON.parse(raw, immutableGreedyReviver) } },\n", | |
" // { name: 'fromJS', f: () => { JSON.parse(raw, (k, v) => Immutable.fromJS(v)) } },\n", | |
" // { name: 'current commutable way', f: () => { commutable.fromJS(JSON.parse(raw)) } },\n", | |
" ]\n", | |
" \n", | |
" for(var test of tests) {\n", | |
" mean = runTrials(test.f, 100)\n", | |
" console.log(_.padEnd(test.name, 30), mean)\n", | |
" }\n", | |
" \n", | |
"\n", | |
"}\n", | |
"\n" | |
], | |
"outputs": [], | |
"execution_count": 15, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:06.452Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"# Evaluating revivers for notebook loading.\n", | |
"\n", | |
"Within nteract we are inevitably going to end up creating an immutable structure. These measurements only make sense in the context of running both the initial `JSON.parse` followed by the transformations. To give it a rough guess, I'll only compare a few I can evaluate." | |
], | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"for(var notebookPath of notebooks) {\n", | |
" console.log(\"\\n ----- \", path.basename(notebookPath))\n", | |
" raw = fs.readFileSync(notebookPath)\n", | |
" \n", | |
" var tests = [\n", | |
" { name: 'straight JSON.parse baseline', f: () => { JSON.parse(raw) } },\n", | |
" { name: 'Object.freeze baseline', f: () => { JSON.parse(raw, (k,v) => Object.freeze(v)) } },\n", | |
" { name: 'immutable greedy nb', f: () => { JSON.parse(raw, immutableGreedyReviver) } },\n", | |
" ]\n", | |
" \n", | |
" for(var test of tests) {\n", | |
" mean = runTrials(test.f, 100)\n", | |
" console.log(_.padEnd(test.name, 50), mean.toString().slice(0,10), 'ms')\n", | |
" }\n", | |
"}" | |
], | |
"outputs": [], | |
"execution_count": 16, | |
"metadata": { | |
"collapsed": false, | |
"outputExpanded": false, | |
"execution": { | |
"shell.execute_reply": "2021-02-07T05:17:06.695Z" | |
} | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"Since these are in milliseconds and the difference is not much, it seems like maybe this doesn't need to be optimized. In the case of the altair notebook, which has a pretty big JSON structure inside (and only one!), perhaps it would make sense if some of our structure is frozen objects (don't force vega payloads to be Immutable Maps).\n", | |
"\n", | |
"```\n", | |
" ----- altair.ipynb\n", | |
"straight JSON.parse baseline 1.10996391 ms\n", | |
"Object.freeze baseline 2.29745900 ms\n", | |
"straight JSON.parse then commutable conversion 6.84918417 ms\n", | |
"immutable greedy nb 5.85418076 ms\n", | |
"```" | |
], | |
"metadata": { | |
"outputExpanded": false | |
} | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [], | |
"metadata": {} | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"name": "node_nteract", | |
"language": "javascript", | |
"display_name": "Node.js (nteract)" | |
}, | |
"kernel_info": { | |
"name": "node_nteract" | |
}, | |
"language_info": { | |
"name": "javascript", | |
"version": "12.8.1", | |
"mimetype": "application/javascript", | |
"file_extension": ".js" | |
}, | |
"title": "Exploring Custom Revival with JSON.parse", | |
"nteract": { | |
"version": "0.8.3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 0 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
public