tensorflow / tensorflow/tensorboard

Improve data loading speed for tensorflow_stub

Open
#4,023 0 comments 0 reactions 2 assignees View on GitHub

@wchargin is already working on this.

Since Aug 14, 2020.

core:backend stat:awaiting tensorflower theme:performance
Dominant language
TypeScript
Stars
7.2k
Forks
1.7k
Avg merge
4d 22h
Merged PRs (30d)
1

Description

Overview

We're using the tensorflow_stub library for data loading (due to not installing tensorflow), on top of an internal cloud Filesystem with non negligible latency(SLA for 1MB read p50 100ms, p90 150ms). We saw data loading speed can be very slow and have big room for improvement.

On the high level here're some of the things we're thinking about:

Below are the profiling results that explains why. The profiling was done on TensorBoard 3.0, seems a lot of changes has happened in https://github.com/tensorflow/tensorboard/issues/1711, but some of our preliminary results shows the same bottleneck still exists with the most recent version.

Profiling code

mul = event_multiplexer.EventMultiplexer()
mul.AddRunsFromDirectory("manifold://xxx", None)
cProfile.runctx("mul.Reload()", globals(), locals(), sort="tottime")

Typical use case 1: Lots of small files

  • Single dir with 546 file * 42k each = 22M total
  • Total time is 413.518s (6.8min)

Profile results

14259238 function calls (14258115 primitive calls) in *413.518* seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.002    0.002  413.534  413.534 {built-in method builtins.exec}
        1    0.000    0.000  413.533  413.533 <string>:1(<module>)
        1    0.000    0.000  413.533  413.533 plugin_event_multiplexer.py:214(Reload)
        1    0.000    0.000  413.533  413.533 plugin_event_multiplexer.py:232(Worker)
        1    0.082    0.082  413.533  413.533 plugin_event_accumulator.py:168(Reload)
    35635    0.011    0.000  409.643    0.011 directory_watcher.py:71(Load)
    35635    0.027    0.000  409.632    0.011 directory_watcher.py:96(_LoadInternal)
     5625    0.022    0.000  398.274    0.071 common.py:77(runWithRetries)
     5706    0.026    0.000  393.933    0.069 THeaderProtocol.py:152(readMessageBegin)
     5625    0.015    0.000  393.897    0.070 srproxy.py:625(readMessageBegin)
     5706    0.021    0.000  393.774    0.069 THeaderTransport.py:228(_reset_protocol)
     5706    0.154    0.000  393.753    0.069 THeaderTransport.py:263(readFrame)
    22824    0.068    0.000  393.183    0.017 TTransport.py:66(readAll)
    22583    0.052    0.000  393.065    0.017 TSocket.py:297(read)
    22502    0.029    0.000  393.052    0.017 srproxy_base.py:179(read)
    22583  393.003    0.017  393.003    0.017 {method 'recv' of '_socket.socket' objects}
     2810    0.016    0.000  268.089    0.095 manifold_client_deprecated.py:79(wrapper)
    36757    0.043    0.000  214.215    0.006 event_file_loader.py:85(Load)
    36757    0.070    0.000  213.981    0.006 event_file_loader.py:49(Load)
    36757    0.154    0.000  213.779    0.006 pywrap_tensorflow.py:190(GetNext)
*  143659    0.150    0.000  208.132    0.001 gfile.py:441(read)
*  1685    0.006    0.000  207.772    0.123 manifoldio.py:54(wrap)
     1685    0.027    0.000  207.766    0.123 manifoldio.py:98(read)
     1685    0.009    0.000  207.627    0.123 manifold_client_deprecated.py:144(get)
     1685    0.020    0.000  207.606    0.123 default.py:300(get)
     1685    0.040    0.000  207.586    0.123 default.py:478(getSerial)
     1685    0.008    0.000  206.252    0.122 BlobStore.py:8251(rangeReadObj)
     1685    0.021    0.000  205.967    0.122 BlobStore.py:8273(recv_rangeReadObj)
      563    0.020    0.000  134.943    0.240 directory_watcher.py:199(_GetNextPath)
      563    0.004    0.000  133.164    0.237 io_wrapper.py:63(ListDirectoryAbsolute)
*    563    0.003    0.000  133.160    0.237 gfile.py:592(listdir)
*    563    0.006    0.000  133.151    0.237 manifoldio.py:199(listdir)
      563    0.088    0.000  133.122    0.236 manifoldio.py:204(<listcomp>)
   316969    0.517    0.000  133.034    0.000 default.py:1024(ls)
     2815    0.009    0.000   83.334    0.030 BlobStore.py:8487(continueBlockEnumeration)
     2815    0.029    0.000   82.951    0.029 BlobStore.py:8505(recv_continueBlockEnumeration)
      562    0.005    0.000   60.445    0.108 directory_watcher.py:177(_SetPath)
      562    0.011    0.000   60.438    0.108 event_file_loader.py:36(__init__)
      562    0.006    0.000   60.423    0.108 pywrap_tensorflow.py:167(__init__)
*    562    0.003    0.000   60.401    0.107 gfile.py:557(exists)
*    562    0.005    0.000   60.391    0.107 manifoldio.py:88(exists)
      562    0.003    0.000   60.354    0.107 manifold_client_deprecated.py:481(exists)
      562    0.002    0.000   60.346    0.107 default.py:1265(exists)
      562    0.005    0.000   60.345    0.107 default.py:541(getMeta)
      562    0.009    0.000   60.339    0.107 default.py:392(getStoredObject)
      562    0.003    0.000   59.918    0.107 BlobStore.py:8215(readObj)
      562    0.007    0.000   59.824    0.106 BlobStore.py:8235(recv_readObj)
      563    0.003    0.000   48.749    0.087 BlobStore.py:8663(startOrderedNodeEnumeration)
      563    0.007    0.000   48.653    0.086 BlobStore.py:8685(recv_startOrderedNodeEnumeration)
    71268    0.086    0.000    5.437    0.000 pywrap_tensorflow.py:42(masked_crc32c)
    71268    0.045    0.000    5.325    0.000 pywrap_tensorflow.py:156(crc32c)
    71268    5.268    0.000    5.268    0.000 pywrap_tensorflow.py:124(crc_update)
    35634    0.259    0.000    3.807    0.000 plugin_event_accumulator.py:268(_ProcessEvent)
     5706    2.243    0.000    3.334    0.001 {built-in method thrift.protocol.fastproto.decode}
    35071    0.091    0.000    3.054    0.000 data_compat.py:32(migrate_value)
     2815    0.023    0.000    2.617    0.001 BlobStore.py:4359(read)
    35028    0.281    0.000    1.964    0.000 data_compat.py:62(make_summary)
     2810    0.015    0.000    1.890    0.001 default.py:256(client)
Composition:
  • 50% on file reading
  • 32% on listdir
  • 15% on exists
Deep dive

File reading:

Listdir:

exists:

How much can we save

Read all files in take 77s, means upper bound saving is 82%. If we read files in parallel it’ll be faster

Typical use case 1: Single big file

  • Two dirs with 131M + 40M = 170M
  • Total time is 55s

Profile results

3881130 function calls (3881126 primitive calls) in 55.790 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000   55.803   55.803 plugin_event_multiplexer.py:225(Reload)
        1    0.000    0.000   55.803   55.803 plugin_event_multiplexer.py:243(Worker)
        2    0.337    0.169   55.803   27.902 plugin_event_accumulator.py:168(Reload)
    32482    0.010    0.000   44.229    0.001 directory_watcher.py:71(Load)
    32482    0.011    0.000   44.219    0.001 directory_watcher.py:96(_LoadInternal)
    32482    0.092    0.000   43.669    0.001 event_file_loader.py:85(Load)
    32482    0.048    0.000   42.391    0.001 event_file_loader.py:49(Load)
    32482    0.148    0.000   42.338    0.001 pywrap_tensorflow.py:190(GetNext)
*    64960    0.091    0.000   36.777    0.001 pywrap_tensorflow.py:42(masked_crc32c)
*    64960    0.088    0.000   36.661    0.001 pywrap_tensorflow.py:156(crc32c)
    64960   36.562    0.001   36.562    0.001 pywrap_tensorflow.py:124(crc_update)
    32480    0.350    0.000   11.237    0.000 plugin_event_accumulator.py:268(_ProcessEvent)
*    32478    0.374    0.000   10.335    0.000 data_compat.py:32(migrate_value)
*    28078    2.672    0.000    9.493    0.000 data_compat.py:69(_migrate_histogram_value)
   129922    0.193    0.000    5.362    0.000 gfile.py:441(read)
       19    0.000    0.000    5.051    0.266 common.py:77(runWithRetries)
       19    0.000    0.000    4.935    0.260 manifold_client_deprecated.py:79(wrapper)
       13    0.000    0.000    4.861    0.374 manifoldio.py:54(wrap)
*       13    0.000    0.000    4.861    0.374 manifoldio.py:98(read)
*       28    0.000    0.000    4.792    0.171 THeaderProtocol.py:152(readMessageBegin)
       28    0.037    0.001    4.791    0.171 THeaderTransport.py:228(_reset_protocol)
       19    0.000    0.000    4.787    0.252 srproxy.py:625(readMessageBegin)
       28    0.080    0.003    4.754    0.170 THeaderTransport.py:263(readFrame)
       13    0.000    0.000    4.728    0.364 manifold_client_deprecated.py:144(get)
       13    0.078    0.006    4.728    0.364 default.py:300(get)
       13    0.000    0.000    4.650    0.358 default.py:478(getSerial)
       13    0.000    0.000    4.519    0.348 BlobStore.py:8251(rangeReadObj)
       13    0.000    0.000    4.515    0.347 BlobStore.py:8273(recv_rangeReadObj)
      112    0.001    0.000    4.459    0.040 TTransport.py:66(readAll)
      171    0.001    0.000    4.347    0.025 TSocket.py:297(read)
Composition:
  • 65% on crc32c
  • 18% on migrate Histogram
  • 9% on file reading
Deep dive

crc32c:

Histogram:

  • We should migrate write path ourselves

file reading:

  • Current buffer size is 16M, so for a 70M file we need 3 read calls; we should support increase buffer size

https://github.com/tensorflow/tensorboard/blob/14bdef2e8f4e97b0aee40b95e5d31e734786f542/tensorboard/compat/tensorflow_stub/io/gfile.py#L52

  • Asyncio?
How much can we save

Reading entire file in take 1.6s, upper bound saving 98%

What we need

We want to get the high level opinion from TensorBoard team on whether there're existing efforts that can already cover these bottlenecks, or should we spend engineering efforts to make some of these happen.

As always, we deeply appreciate all the help and support we received from TensorBoard team & community, and look forward to this discussion!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.