Skip to content

Instantly share code, notes, and snippets.

@maartenbreddels
Created May 10, 2017 17:44
Show Gist options
  • Save maartenbreddels/40fa030fdb922e6d2074282ceed6b753 to your computer and use it in GitHub Desktop.
Save maartenbreddels/40fa030fdb922e6d2074282ceed6b753 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Ipywidgets benchmark\n",
"This notebooks benchmarks shows how much time it takes for 1M floats to be transferred to the frontend (browser) using the binary transfer vs json.\n",
"This only is reliable if frontend and backend are on the same machine (or time synchronized). Otherwise only trust the roundtrip time"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import ipywidgets as widgets\n",
"import IPython\n",
"from traitlets import Unicode, validate, CInt, CFloat\n",
"from traittypes import Array\n",
"import numpy as np\n",
"import time\n",
"def from_json(value, obj=None):\n",
" return []\n",
"\n",
"def array_to_binary(ar, obj=None):\n",
" if ar is not None:\n",
" ar = ar.astype(np.float32)\n",
" mv = memoryview(ar)\n",
" # 'nested' is just to show the (de)serialization goes all fine\n",
" return {'data': mv, 'shape': ar.shape}\n",
" else:\n",
" return None\n",
"\n",
"last_value = None\n",
"setters = 0\n",
"def binary_to_array(value, obj=None):\n",
" global last_value, setters\n",
" setters += 1\n",
" #print(\">>\", value) # print msg'es get lost, but check the websocket output\n",
" last_value = value # or keep a reference to a global for debugging\n",
" return np.frombuffer(value['data'], dtype=np.float32)\n",
" #return np.frombuffer(value['data'], dtype=np.float32)\n",
"\n",
"def array_to_list(ar, obj=None):\n",
" if ar is None:\n",
" return None\n",
" return ar.tolist()\n",
"\n",
"def list_to_array(list, obj=None):\n",
" return np.array(list, dtype=np.float32)\n",
"array_binary_serialization = dict(to_json=array_to_binary, from_json=binary_to_array)\n",
"array_json_serialization = dict(to_json=array_to_list, from_json=list_to_array)\n",
"\n",
"class BenchmarkWidget(widgets.DOMWidget):\n",
" _model_name = Unicode('BenchmarkWidgetModel').tag(sync=True)\n",
" _model_module = Unicode('benchmark').tag(sync=True)\n",
" x = Array(default_value=None, allow_none=True).tag(sync=True, **array_binary_serialization)\n",
" x2 = Array(default_value=None, allow_none=True).tag(sync=True, **array_binary_serialization)\n",
" xtime = CFloat(default_value=None, allow_none=True).tag(sync=True)\n",
" y = Array(default_value=None, allow_none=True).tag(sync=True, **array_json_serialization)\n",
" y2 = Array(default_value=None, allow_none=True).tag(sync=True, **array_json_serialization)\n",
" ytime = CFloat(default_value=None, allow_none=True).tag(sync=True)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": [
"require.undef('benchmark');\n",
"\n",
"define('benchmark', [\"jupyter-js-widgets\"], function(widgets) {\n",
"\n",
" function deserialize_numpy_array(data, manager) {\n",
" if(data == null)\n",
" return null;\n",
" console.log(\"binary array\")\n",
" window.last_data = data\n",
" var ar = new Float32Array(data.data.buffer)\n",
" window.last_array = ar\n",
" return {data:ar, shape:data.shape, nested:data.nested}\n",
" }\n",
" \n",
" function serialize_numpy_array(data, m) {\n",
" console.log(\"serialize\")\n",
" return data;//[0,9]\n",
" }\n",
" \n",
" // Define the HelloView\n",
" var BenchmarkWidgetModel = widgets.WidgetModel.extend({\n",
" defaults: function() {\n",
" return _.extend(widgets.WidgetModel.prototype.defaults(), {\n",
" _model_name : 'BenchmarkWidgetModel',\n",
" _model_module : 'benchmark',\n",
" })\n",
" },\n",
" initialize: function(attributes, options) {\n",
" console.log(this)\n",
" window.last_doubler = this\n",
" BenchmarkWidgetModel.__super__.initialize.apply(this, arguments);\n",
" var copy = function(from_name, to_name, time_name) {\n",
" return function() {\n",
" console.log(\"copy\", from_name, to_name)\n",
" this.set(time_name, Date.now())\n",
" this.set(to_name, this.get(from_name))\n",
" this.save_changes()\n",
" }\n",
" }\n",
" this.on('change:x', copy('x', 'x2', 'xtime'), this)\n",
" this.on('change:y', copy('y', 'y2', 'ytime'), this)\n",
" },\n",
" }, {\n",
" serializers: _.extend({\n",
" x: { deserialize: deserialize_numpy_array, serialize: serialize_numpy_array },\n",
" x2: { deserialize: deserialize_numpy_array, serialize: serialize_numpy_array },\n",
" }, widgets.WidgetModel.serializers)\n",
"});\n",
"\n",
" \n",
" return {\n",
" BenchmarkWidgetModel: BenchmarkWidgetModel\n",
" }\n",
"});"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%javascript\n",
"require.undef('benchmark');\n",
"\n",
"define('benchmark', [\"jupyter-js-widgets\"], function(widgets) {\n",
"\n",
" function deserialize_numpy_array(data, manager) {\n",
" if(data == null)\n",
" return null;\n",
" console.log(\"binary array\")\n",
" window.last_data = data\n",
" var ar = new Float32Array(data.data.buffer)\n",
" window.last_array = ar\n",
" return {data:ar, shape:data.shape, nested:data.nested}\n",
" }\n",
" \n",
" function serialize_numpy_array(data, m) {\n",
" console.log(\"serialize\")\n",
" return data;//[0,9]\n",
" }\n",
" \n",
" // Define the HelloView\n",
" var BenchmarkWidgetModel = widgets.WidgetModel.extend({\n",
" defaults: function() {\n",
" return _.extend(widgets.WidgetModel.prototype.defaults(), {\n",
" _model_name : 'BenchmarkWidgetModel',\n",
" _model_module : 'benchmark',\n",
" })\n",
" },\n",
" initialize: function(attributes, options) {\n",
" console.log(this)\n",
" window.last_doubler = this\n",
" BenchmarkWidgetModel.__super__.initialize.apply(this, arguments);\n",
" var copy = function(from_name, to_name, time_name) {\n",
" return function() {\n",
" console.log(\"copy\", from_name, to_name)\n",
" this.set(time_name, Date.now())\n",
" this.set(to_name, this.get(from_name))\n",
" this.save_changes()\n",
" }\n",
" }\n",
" this.on('change:x', copy('x', 'x2', 'xtime'), this)\n",
" this.on('change:y', copy('y', 'y2', 'ytime'), this)\n",
" },\n",
" }, {\n",
" serializers: _.extend({\n",
" x: { deserialize: deserialize_numpy_array, serialize: serialize_numpy_array },\n",
" x2: { deserialize: deserialize_numpy_array, serialize: serialize_numpy_array },\n",
" }, widgets.WidgetModel.serializers)\n",
"});\n",
"\n",
" \n",
" return {\n",
" BenchmarkWidgetModel: BenchmarkWidgetModel\n",
" }\n",
"});"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "ddef66597b9248cd8bfccebab0900307",
"version_major": "2",
"version_minor": "0"
},
"text/plain": [
"A Jupyter Widget"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"x = np.arange(1e6, dtype=np.float32)\n",
"bench = BenchmarkWidget()\n",
"output = widgets.Output()\n",
"def received(o, *args):\n",
" global time_received\n",
" time_received = time.time()\n",
" with output:\n",
" print(\"received\")\n",
" #print(o)\n",
"bench.observe(received, [\"x2\", \"y2\"])\n",
"IPython.display.display(output)"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [],
"source": [
"#%%prun\n",
"time_send = time.time()\n",
"bench.x = x\n",
"#time.sleep(1)"
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"using binary transfer\n",
"roundtrip time 1.891538143157959\n",
"python->js 1.312333106994629\n",
"js->python 0.5792050361633301\n"
]
}
],
"source": [
"# wait till your see received printed out above\n",
"dt = time_received - time_send\n",
"dt_to_js = bench.xtime/1000 - time_send\n",
"print(\"using binary transfer\")\n",
"print(\"roundtrip time \", dt)\n",
"print(\"python->js \", dt_to_js)\n",
"print(\"js->python \", dt-dt_to_js)\n"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"time_send = time.time()\n",
"bench.y = x"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"using json\n",
"roundtrip time 11.392460107803345\n",
"python->js 7.879271984100342\n",
"js->python 3.513188123703003\n"
]
}
],
"source": [
"# wait till your see received printed out above\n",
"dt_json = time_received - time_send\n",
"dt_to_js_json = bench.ytime/1000 - time_send\n",
"print(\"using json\")\n",
"print(\"roundtrip time \", dt_json)\n",
"print(\"python->js \", dt_to_js_json)\n",
"print(\"js->python \", dt_json-dt_to_js_json)"
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"speedup (roundtrip) 6.0228550764424\n",
"speedup (python->js) 6.004018295434644\n",
"speedup (js->python) 6.065534490125391\n"
]
}
],
"source": [
"print(\"speedup (roundtrip) \", dt_json/dt)\n",
"print(\"speedup (python->js)\", dt_to_js_json/dt_to_js)\n",
"print(\"speedup (js->python)\", (dt_json-dt_to_js_json)/(dt-dt_to_js))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python [default]",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@LustigePerson
Copy link

Great comparison!
Do you know if there is as "binary option" also for transfer of the ipynb files?

@maartenbreddels
Copy link
Author

Thanks, no, not that I know off, but I think that does not go through the websocket, but a REST API I think.

@LustigePerson
Copy link

Thanks for your answer. And yes, as far as I understand it uses the rest API which renders the availbale --NotebookApp.websocket_compression_options option useless for this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment