Repository: bokeh/bokeh

Issue:
Repeatedly displaying a plot degrades performance
After encountering some performance issues in https://github.com/bokeh/bokeh/pull/5976 @bryevdv discovered that there were already some performance regressions on master. This could be observed by running the last three cells in``examples/howto/Linked Panning.ipynb``, which becomes considerably slower on each execution on current master.

After @bryevdv narrowed it down to a change between 0.12.5dev13 and 0.12.5dev14 I ran a git bisect, which came up with the following commit as the culprit:

>65643839fcb8065e1ffc926c2295e380e4637e4a is the first bad commit
commit 65643839fcb8065e1ffc926c2295e380e4637e4a
Date:   Wed Mar 1 13:32:51 2017 -0500


Discussion:
After one show:


<img width="755" alt="screen shot 2017-03-22 at 12 30 16" src="https://cloud.githubusercontent.com/assets/1078448/24211959/bbe2510e-0efb-11e7-8268-5e20b66b618e.png">

After two:

<img width="799" alt="screen shot 2017-03-22 at 12 30 32" src="https://cloud.githubusercontent.com/assets/1078448/24211971/c64c6666-0efb-11e7-8624-a1d01b33f54c.png">

Everythign basically doubles. Best immediate guess is that now work is somehow getting done on all models ever shown, not just the ones being immediately shown. 
OK, before, the notebook path used ModelIn*Empty*Document, which does something slightly different. This patch below shows dramatic improvement. I am still seeing a fixed increase of 20-30 ms on each repeated show, but that's much better than before. 
```

diff --git a/bokeh/embed.py b/bokeh/embed.py
index 19d4eaf44..d3e2f6ac9 100644
--- a/bokeh/embed.py
+++ b/bokeh/embed.py
@@ -76,6 +76,28 @@ def _ModelInDocument(models, apply_theme=None):
         doc.remove_root(model, apply_theme)
     doc.theme = old_theme

+@contextmanager
+def _ModelInEmptyDocument(model, apply_theme=None):
+    from .document import Document
+    doc = _find_existing_docs([model])
+
+    if apply_theme is FromCurdoc:
+        from .io import curdoc; curdoc
+        doc.theme = curdoc().theme
+    elif apply_theme is not None:
+        doc.theme = apply_theme
+
+    model._document = None
+    for ref in model.references():
+        ref._document = None
+    empty_doc = Document()
+    empty_doc.add_root(model)
+
+    yield model
+
+    model._document = doc
+    for ref in model.references():
+        ref._document = doc

 def _find_existing_docs(models):
     existing_docs = set(m if isinstance(m, Document) else m.document for m in models)
@@ -330,7 +352,7 @@ def notebook_div(model, notebook_comms_target=None, theme=FromCurdoc):
     model = _check_one_model(model)

     # Append models to one document. Either pre-existing or new and render
-    with _ModelInDocument([model], apply_theme=theme):
+    with _ModelInEmptyDocument(model, apply_theme=theme):
         (docs_json, render_items) = _standalone_docs_json_and_render_items([model])

     item = render_items[0]
```


Resolving patch:
diff --git a/bokeh/embed.py b/bokeh/embed.py
--- a/bokeh/embed.py
+++ b/bokeh/embed.py
@@ -76,6 +76,28 @@ def _ModelInDocument(models, apply_theme=None):
         doc.remove_root(model, apply_theme)
     doc.theme = old_theme
 
+@contextmanager
+def _ModelInEmptyDocument(model, apply_theme=None):
+    from .document import Document
+    doc = _find_existing_docs([model])
+
+    if apply_theme is FromCurdoc:
+        from .io import curdoc; curdoc
+        doc.theme = curdoc().theme
+    elif apply_theme is not None:
+        doc.theme = apply_theme
+
+    model._document = None
+    for ref in model.references():
+        ref._document = None
+    empty_doc = Document()
+    empty_doc.add_root(model)
+
+    yield model
+
+    model._document = doc
+    for ref in model.references():
+        ref._document = doc
 
 def _find_existing_docs(models):
     existing_docs = set(m if isinstance(m, Document) else m.document for m in models)
@@ -330,7 +352,7 @@ def notebook_div(model, notebook_comms_target=None, theme=FromCurdoc):
     model = _check_one_model(model)
 
     # Append models to one document. Either pre-existing or new and render
-    with _ModelInDocument([model], apply_theme=theme):
+    with _ModelInEmptyDocument(model, apply_theme=theme):
         (docs_json, render_items) = _standalone_docs_json_and_render_items([model])
 
     item = render_items[0]


Test patch: