Skip to content

Instantly share code, notes, and snippets.

@dveeden
Created December 26, 2015 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dveeden/ff407dd603c3e74484b1 to your computer and use it in GitHub Desktop.
Save dveeden/ff407dd603c3e74484b1 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"After reading about the difference between MySQL Connector/Python and MySQLdb on [this blog post](http://charlesnagy.info/it/python/python-mysqldb-vs-mysql-connector-query-performance) I wondered how the C Extension option in Connector/Python would perform.\n",
"\n",
"If you want to run the code yourself you'll need: Jupyter/IPython, Python 3, Requests, MySQLdb, Connector/Python, Matplotlib, Pandas and MySQL."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%matplotlib notebook"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Using mysql.connector 2.1.3\n",
"Using MySQLdb 1.3.7\n",
"C Extension for MySQL Connector/Python available: True\n"
]
}
],
"source": [
"import random\n",
"import gzip\n",
"import time\n",
"\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"import requests\n",
"import mysql.connector\n",
"import MySQLdb\n",
"for imp in [mysql.connector, MySQLdb]:\n",
" print('Using {imp} {version}'.format(imp=imp.__name__, version=imp.__version__))\n",
"print('C Extension for MySQL Connector/Python available: %s' % mysql.connector.HAVE_CEXT)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Make sure the C Extension is available. This needs MySQL Connector/Python 2.1 or newer. On Fedora you might need to install this with `dnf install mysql-connector-python3-cext` if you have the mysql-connectors-community repository installed. If you compile from source then make sure to use the `--with-mysql-capi` option."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"worlddb_url = 'https://downloads.mysql.com/docs/world.sql.gz'\n",
"worlddb_req = requests.get(worlddb_url)\n",
"if worlddb_req.status_code == 200:\n",
" worldsql = gzip.decompress(worlddb_req.content).decode('iso-8859-15')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"config = {\n",
" 'host': '127.0.0.1',\n",
" 'port': 5710,\n",
" 'user': 'msandbox',\n",
" 'passwd': 'msandbox',\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The above is my config to connect to a MySQL Sandbox running MySQL Server 5.7.10.\n",
"\n",
"Note: you might hit [MySQL Bug #79780](https://bugs.mysql.com/bug.php?id=79780) when loading\n",
"the world database into MySQL with Connector/Python with the C Extension enabled."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"c1 = mysql.connector.connect(use_pure=False, **config)\n",
"cur1 = c1.cursor()\n",
"cur1.execute('DROP SCHEMA IF EXISTS world')\n",
"cur1.execute('CREATE SCHEMA world DEFAULT CHARACTER SET latin1')\n",
"cur1.execute('USE world')\n",
"result = [x for x in cur1.execute(worldsql, multi=True)]\n",
"cur1.close()\n",
"c1.close()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"config['db'] = 'world'"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"perfdata = pd.DataFrame(columns=['connpy','connpy_cext','MySQLdb'], index=range(10000))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we're going to run 10000 queries with a random primary key between 1 and 8000. This does not use the C Extension as use_pure is set to True."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [],
"source": [
"c1 = mysql.connector.connect(use_pure=True, **config)\n",
"cur1 = c1.cursor()\n",
"for it in range(10000):\n",
" city_id = random.randint(1,8000)\n",
" start = time.perf_counter()\n",
" cur1.execute(\"SELECT * FROM City WHERE ID=%s\", (city_id,))\n",
" cur1.fetchone()\n",
" perfdata.ix[it]['connpy'] = time.perf_counter() - start"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next up is Connector/Python with the C Extension (use_pure=False and HAVE_CEXT indicates we have the C Extension available)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [],
"source": [
"c1 = mysql.connector.connect(use_pure=False, **config)\n",
"cur1 = c1.cursor()\n",
"for it in range(10000):\n",
" city_id = random.randint(1,8000)\n",
" start = time.perf_counter()\n",
" cur1.execute(\"SELECT * FROM City WHERE ID=%s\", (city_id,))\n",
" cur1.fetchone()\n",
" perfdata.ix[it]['connpy_cext'] = time.perf_counter() - start"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And last, but not least, MySQLdb."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"c2 = MySQLdb.connect(**config)\n",
"cur2 = c2.cursor()\n",
"for it in range(10000):\n",
" city_id = random.randint(1,8000)\n",
" start = time.perf_counter()\n",
" cur2.execute(\"SELECT * FROM City WHERE ID=%s\", (city_id,))\n",
" cur2.fetchone()\n",
" perfdata.ix[it]['MySQLdb'] = time.perf_counter() - start"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's have a look to what our data looks like"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>connpy</th>\n",
" <th>connpy_cext</th>\n",
" <th>MySQLdb</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>0.00145918</td>\n",
" <td>0.000354935</td>\n",
" <td>0.000353173</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>0.000907707</td>\n",
" <td>0.000243508</td>\n",
" <td>0.000249597</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>0.000468397</td>\n",
" <td>0.000277101</td>\n",
" <td>0.000207893</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>0.000595066</td>\n",
" <td>0.000241349</td>\n",
" <td>0.00020754</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>0.000641848</td>\n",
" <td>0.000258027</td>\n",
" <td>0.000193182</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" connpy connpy_cext MySQLdb\n",
"0 0.00145918 0.000354935 0.000353173\n",
"1 0.000907707 0.000243508 0.000249597\n",
"2 0.000468397 0.000277101 0.000207893\n",
"3 0.000595066 0.000241349 0.00020754\n",
"4 0.000641848 0.000258027 0.000193182"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"perfdata.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's plot that"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('<div/>');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" fig.waiting = false;\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" this.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
" 'ui-helper-clearfix\"/>');\n",
" var titletext = $(\n",
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
" 'text-align: center; padding: 3px;\"/>');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('<div/>');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('<canvas/>');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var rubberband = $('<canvas/>');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width);\n",
" canvas.attr('height', height);\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('<button/>');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('<span/>');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('<span/>');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('<span/>');\n",
"\n",
" var fmt_picker = $('<select/>');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'];\n",
" var y0 = fig.canvas.height - msg['y0'];\n",
" var x1 = msg['x1'];\n",
" var y1 = fig.canvas.height - msg['y1'];\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x;\n",
" var y = canvas_pos.y;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n",
" fig.send_message('closing', {});\n",
" fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
" var button = $('<button class=\"btn btn-mini btn-danger\" href=\"#\" title=\"Close figure\"><i class=\"fa fa-times icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Close figure', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i<ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code'){\n",
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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WBa1NEjWO4jFyK3P2bNns1Kq5HJr1qzhuXPn5me/7rjjjvxnucH9jjvu4Hnz5vG4ceP4lFNO4Z6envwyRMQXXHABX3rppTxhwgSePHly3lDl2LBhA59zzjk8efJknjZtGl9yySW8ffv2/OebN2/miy++mGfOnMnjxo3j4447jr///e/z2rVrefTo0Txq1Kj8zNSTTz7JnufxD3/4Q54/fz63tLTwzJkz+YorruCdO3fm1zljxgxeuXIlL1y4kMeNG8fXX389e57He/bs4SlTpvDPfvYz9rxCA5hj4sSJfOONN/JXv/pVnjVrVsFnO3bs4AkTJvD999/PZ599Njc0NPCYMWO4sbGR586dy573jgFcvXo1T506lVtaWviiiy7i3bt359fT09PDZ511Fk+cOJGPPPJIvuiii3jr1q35z1tbW3n58uXc3t7OTU1NPH36dL711lt949jf38+HHnoor1mzpqx2UsoArlu3jseOHcvr169nzztoks8+++z8DOHhhx/OmUyGx44dy42NjXzxxReX1XdgAKFIggGURS0NkvUOYiGvPnfs3sHPv/p8ouXs7e3lTCbDDz/8cOBy9913H48fP54fffRRHhoa4ocffpgbGxv5/vvvZ897xwCeccYZ/NJLL/GOHTt48eLF3Nraml8HEfFhhx3G99xzDw8NDfGPfvQjPvTQQ3ndunXseR5v2rSJJ06cyDfddBO//vrrvGnTJj799NN52bJl7HkHDcWCBQv4nHPO4V/+8pfseR4/99xzeZO5atUqXrBgQUG5+/v7+fDDD+ebb76Zd+3axb/4xS/4+OOP5xUrVuSXmTFjBk+dOpV//OMfs+d5ecP5+OOP8zHHHJNfrrW1la+88kr2vIOzhHfeeSc3NDTwE088wdu3b+fm5mZ+6KGH8suvXbuWp0+fzkNDQ+x5B2cAbYeADz30UL7hhht4165d3Nvbyy0tLXlztm3bNp42bRp//OMf59dee403btzIra2t/KEPfaigXE1NTfl6/OY3v8mjRo3ynWm77bbbeNSoUfz666+X1VbCzAB+4Qtf4BkzZvDmzZt59erVPGvWrLxZfeGFF3AIGEpPMICygOmQA2JR3frcvHMzD+4YDFymGgbwySef5Ewmwxs2bAhc7swzz+TLLrus4L3ly5fzWWedxZ73jgF84okn8p9/97vf5cMPPzz/moj4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L36RM5kM9/b28owZM/iOO+4o+O4XvvAFfu9738ue5/Ff//Vf8/Lly31/59VXX+WWlha+6qqrij7LGbj169ez55U2gD/96U/55JNP5sbGRj7xxBP5hhtusBrAK664go844gieMmUKf+ITn+Bdu3alXt+gNDCAUCKCAZRFrQySIwHEQmZ9Jp2rWltb+corr+SVK1fy3/zN37DnefzEE0/wjBkz+LnnnuNMJsN9fX187bXX8vvf//7894aGhvhd73pX/tDxDTfcwE1NTfyZz3yG161bx1u3bi34ne9///uBh5OnTZvGV199NXtesAHcunUrT5w4ka+++mp+/fXXuaenh2fNmsUNDQ35ZVauXMmHHnpofpkNGzbwMcccw5/97GdTbxegNDCAUCKCAZQFTIccEAt59fnKK29wX9/+RPNVa2srX3XVVfziiy/y+PHjecuWLdze3s6f/exn+fnnn8+btpdeeokPO+ww7u7uZs/z+JFHHuFx48bxq6++ml/X17/+dT733HN50qRJfMghh/Dpp5/OPT097Hker127ljOZDO/YscNajpNPPpk//OEPs+cFG8C1a9fy5MmTeWhoKP9ebqYy93rlypU8ZcqUgmVuvfVWnjVrVurtApQGBhBKRDCAsoDpkANiUd36/OUv3+T+/rdKrmfLln8rucxrr/2+4nLmDKDneXzeeefxpz/9aW5sbOSXXnqpwAB6nsdLly7NH+ZdsmRJ3rDZ6Onp4dNOO41nzpzJnvfODGBfX591+WnTpvHKlSvZ84IN4HXXXccnnnhiwXvf+c53igygucz3vvc9Hj16dOrtApQGBhBKRDCAsoDpkANiUd363Lz533hw8N9j+a2+vv28eXNl69IN4IMPPsgNDQ28ZMkS9jyvyAA+/vjj3NLSwi+88AKPHj2af/aznwWu+9vf/jZnMhn+zW9+w6+++io3NzfnD/PqPPzww5zJZPiHP/whe16wAbzrrrt48uTJvGfPnvx7thlAcxnMANYOMIBQIoIBlAVMhxwQi9qtzyj5LHcOYO71o48+ygMDA+x5xQbQ8zyeO3cun3DCCXzyyScXrOcLX/gCP/DAA7x582b2PI/7+/v5rLPO4ne/+935Zb72ta/xmDFj+KabbuJf//rXvH37dv7ud7/L06ZN4/b29vxyOQO4Y8cOfu211/Ls2rUrfw7gypUreceOHbxh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\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<matplotlib.legend.Legend at 0x7f47bd8c2518>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"plt.style.use('ggplot')\n",
"plt.scatter(perfdata.index, perfdata.connpy, s=1, c='r',\n",
" label='Connector/Python Pure')\n",
"plt.scatter(perfdata.index, perfdata.connpy_cext, s=1, c='g',\n",
" label='Connector/Python C Ext')\n",
"plt.scatter(perfdata.index, perfdata.MySQLdb, s=1, c='b',\n",
" label='MySQLdb')\n",
"plt.ylim(ymin=0, ymax=0.001)\n",
"plt.xlim(xmin=0, xmax=10000)\n",
"plt.xlabel('Run #')\n",
"plt.ylabel('Runtime in seconds')\n",
"plt.legend()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The performance of MySQL Connector/Python 2.1 with the C Extension is much closer to MySQLdb.\n",
"\n",
"There is one serious drawback of using the C Extension: Prepared statements are not yet supported.\n",
"\n",
"But there is more that performance alone. I like the MySQL Connector/Python API over the MySQLdb API and this shows that Connector/Python is flexible and can be almost as fast.\n",
"\n",
"The pure Python implementation also has advantages: Easier installation (no C compiler required) and the option to use alternative implementations like PyPy"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.4.3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment