{"action":"created","issue":{"url":"https://api.github.com/repos/bokeh/bokeh/issues/5504","repository_url":"https://api.github.com/repos/bokeh/bokeh","labels_url":"https://api.github.com/repos/bokeh/bokeh/issues/5504/labels{/name}","comments_url":"https://api.github.com/repos/bokeh/bokeh/issues/5504/comments","events_url":"https://api.github.com/repos/bokeh/bokeh/issues/5504/events","html_url":"https://github.com/bokeh/bokeh/pull/5504","id":193363568,"number":5504,"title":"Base64 serialization","user":{"login":"philippjfr","id":1550771,"avatar_url":"https://avatars.githubusercontent.com/u/1550771?v=3","gravatar_id":"","url":"https://api.github.com/users/philippjfr","html_url":"https://github.com/philippjfr","followers_url":"https://api.github.com/users/philippjfr/followers","following_url":"https://api.github.com/users/philippjfr/following{/other_user}","gists_url":"https://api.github.com/users/philippjfr/gists{/gist_id}","starred_url":"https://api.github.com/users/philippjfr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/philippjfr/subscriptions","organizations_url":"https://api.github.com/users/philippjfr/orgs","repos_url":"https://api.github.com/users/philippjfr/repos","events_url":"https://api.github.com/users/philippjfr/events{/privacy}","received_events_url":"https://api.github.com/users/philippjfr/received_events","type":"User","site_admin":false},"labels":[{"id":88661915,"url":"https://api.github.com/repos/bokeh/bokeh/labels/status:%20WIP","name":"status: WIP","color":"fef2c0","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":6,"created_at":"2016-12-04T17:35:15Z","updated_at":"2016-12-12T20:00:36Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/bokeh/bokeh/pulls/5504","html_url":"https://github.com/bokeh/bokeh/pull/5504","diff_url":"https://github.com/bokeh/bokeh/pull/5504.diff","patch_url":"https://github.com/bokeh/bokeh/pull/5504.patch"},"body":"This PR implements some of the suggestions in https://github.com/bokeh/bokeh/issues/2204. In further discussion and initial testing (see https://github.com/bokeh/bokeh/pull/5429) we found that simple base64 encoding offers huge performance gains compared to the current implementation. A more complex binary json format is therefore not necessary at this time.\r\n\r\n## Motivation\r\n\r\nCurrently sending large arrays to the browser can take a very long time, particularly when sending relatively large images. This is because all data is sent as raw strings, which is highly inefficient both in terms of size of the data that has to be sent to the frontend and in terms of processing because both backend and frontend iterate over the arrays to validate them. For a simple 2x2 image column data source the JSON representation would previously have looked like this:\r\n\r\n```\r\n{'image': [[[0.19593542932534513, 0.8104239473495675], [0.3907588555073921, 0.15267810064366616]]]}\r\n```\r\n\r\nthe new JSON representation looks as follows:\r\n\r\n```\r\n{'image': [{'data': 'yKTPhb3b2j/Qy4BxlXzvP1bfkgIid9Q/kz0MlC4F4T8=', shape: [2,2], dtype: 'float64'}]}\r\n```\r\n\r\nIn the case of a 2000x2000 image it took up to 30 seconds to serialize and send the image. Through initial investigation we found that simple base64 encoding of arrays can result in huge performance gains for sending array data via web sockets. Overall this has the potential to bring 10-50x improvements in sending large arrays via the comms in the notebook and will also offer some more limited improvements for bokeh server.\r\n\r\n## Design\r\n\r\n1. The encoding should happen as a last step during serialization on the Python end and as the first step after deserialization on the JS end, ensuring no other code has to deal with base64 encoded data avoiding any backward compatibility issues.\r\n2. The data should be wrapped in a simple JS object along ith the shape and type information. A dedicated model might be considered in future but is not necessary.\r\n3. The column data source on the fronted will unpack and store the shape information on the _shapes attribute.\r\n4. The encoding behavior should be configurable, e.g. do not encode unless the array is larger than a configurable size.\r\n5. All numeric column data source data should potentially be encoded, i.e. both 1-D and 2-D arrays.\r\n\r\n## Caveats\r\n\r\nCurrently this PR will correctly encode and decode any numerical types except for numpy int64 arrays. Since there is no native 64-bit integer type in Javascript (internally ints are handled as double precision floats where the maximum safe integer is 2**53), we cannot directly encode and decode 64-bit integer data. Using the npm ``int64-buffer`` package I managed to decode an ``int64`` ArrayBuffer but the advantages and disadvantages of various approaches should be evaluated in a subsequent PR:\r\n\r\n```coffeescript\r\nview = new Int32Array(bytes)\r\narray = []\r\nfor i in [0...view.length/2]\r\n  int = new Int64LE(view.buffer.slice(i*8,(i+1)*8))\r\n  array.push(int)\r\n```\r\n\r\n## To-Do\r\n\r\n- [x] Add configurable options ``BOKEH_USE_BINARY_ARRAYS`` and ``BOKEH_BINARY_ARRAY_CUTOFF``\r\n- [ ] Handle patch events\r\n- [ ] Handle stream events\r\n- [ ] Ensure current tests pass and add extensive tests\r\n- [ ] Add release document entry (if new feature or API change)\r\n- [x] Document environment variables"},"comment":{"url":"https://api.github.com/repos/bokeh/bokeh/issues/comments/266536188","html_url":"https://github.com/bokeh/bokeh/pull/5504#issuecomment-266536188","issue_url":"https://api.github.com/repos/bokeh/bokeh/issues/5504","id":266536188,"user":{"login":"bryevdv","id":1078448,"avatar_url":"https://avatars.githubusercontent.com/u/1078448?v=3","gravatar_id":"","url":"https://api.github.com/users/bryevdv","html_url":"https://github.com/bryevdv","followers_url":"https://api.github.com/users/bryevdv/followers","following_url":"https://api.github.com/users/bryevdv/following{/other_user}","gists_url":"https://api.github.com/users/bryevdv/gists{/gist_id}","starred_url":"https://api.github.com/users/bryevdv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bryevdv/subscriptions","organizations_url":"https://api.github.com/users/bryevdv/orgs","repos_url":"https://api.github.com/users/bryevdv/repos","events_url":"https://api.github.com/users/bryevdv/events{/privacy}","received_events_url":"https://api.github.com/users/bryevdv/received_events","type":"User","site_admin":false},"created_at":"2016-12-12T20:00:36Z","updated_at":"2016-12-12T20:00:36Z","body":"@philippjfr that's strange but thanks for noting, I can try to take a look too"}}
{"actor":{"display_login":"bryevdv"}}