performance.rst 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. Performance Benchmark Report
  2. ============================
  3. The purpose of this document is to provide very broad performance measurements
  4. and comparison between Lakesuperior and Fedora/Modeshape implementations.
  5. Lakesuperior v1.0a17 and v1.0a18 were taken into consideration. This is because
  6. of the extensive reworking of the whole architecture and complete rewrite
  7. of the storage layer, that led to significant performance gains.
  8. Environment
  9. -----------
  10. Hardware
  11. ~~~~~~~~
  12. ‘Rather Snappy’ Laptop
  13. ^^^^^^^^^^^^^^^^^^^^^^
  14. - Dell Latitude 7490 Laptop
  15. - 8x Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz
  16. - 16Gb RAM
  17. - SSD
  18. - Arch Linux OS
  19. - glibc 2.26-11
  20. - python 3.7.0
  21. - lmdb 0.9.22
  22. The laptop was left alone during the process, but some major applications
  23. (browser, email client, etc.) were left open.
  24. ‘Ole Workhorse’ server
  25. ^^^^^^^^^^^^^^^^^^^^^^
  26. - 8x Intel(R) Xeon(R) CPU X5550 @ 2.67GHz
  27. - 16Gb RAM
  28. - Magnetic drive, XXX RPM
  29. Benchmark script
  30. ~~~~~~~~~~~~~~~~
  31. `Generator script <../../util/benchmark.py>`__
  32. The script was run with default values: resprectively 10,000 and 100,000
  33. children under the same parent. PUT and POST requests were tested separately.
  34. The script calculates only the timings used for the PUT or POST requests, not
  35. counting the time used to generate the graphs.
  36. Data Set
  37. ~~~~~~~~
  38. Synthetic graph created by the benchmark script. The graph is unique for
  39. each request and consists of 200 triples which are partly random data,
  40. with a consistent size and variation:
  41. - 50 triples have an object that is a URI of an external resource (50
  42. unique predicates; 5 unique objects).
  43. - 50 triples have an object that is a URI of a repository-managed
  44. resource (50 unique predicates; 5 unique objects).
  45. - 100 triples have an object that is a 64-character random Unicode
  46. string (50 unique predicates; 100 unique objects).
  47. LDP Data Retrieval
  48. ~~~~~~~~~~~~~~~~~~
  49. REST API request::
  50. time curl http://localhost:8000/ldp/pomegranate > /dev/null
  51. SPARQL Query
  52. ~~~~~~~~~~~~
  53. *Note:* The query may take a long time and therefore is made on the
  54. single-threaded server (``lsup-server``) that does not impose a timeout (of
  55. course, gunicorn could also be used by changing the configuration to allow a
  56. long timeout).
  57. Sample query::
  58. PREFIX ldp: <http://www.w3.org/ns/ldp#>
  59. SELECT (COUNT(?s) AS ?c) WHERE {
  60. ?s a ldp:Resource .
  61. ?s a ldp:Container .
  62. }
  63. Raw request::
  64. time curl -iXPOST -H'Accept:application/sparql-results+json' \
  65. -H'Content-Type:application/x-www-form-urlencoded; charset=UTF-8' \
  66. -d 'query=PREFIX+ldp:+<http://www.w3.org/ns/ldp#> SELECT+(COUNT(?s)+AS+?c)'\
  67. '+WHERE+{ ++?s+a+ldp:Resource+. ++?s+a+ldp:Container+. }+' \
  68. http://localhost:5000/query/sparql
  69. Python API Retrieval
  70. ~~~~~~~~~~~~~~~~~~~~
  71. In order to illustrate the advantages of the Python API, a sample retrieval of
  72. the container resource after the load has been timed. This was done in an
  73. IPython console::
  74. In [1]: from lakesuperior import env_setup
  75. In [2]: from lakesuperior.api import resource as rsrc_api
  76. In [3]: %timeit x = rsrc_api.get('/pomegranate').imr
  77. Results
  78. -------
  79. .. _rather-snappy-laptop-1:
  80. ‘Rather Snappy’ Laptop
  81. ~~~~~~~~~~~~~~~~~~~~~~
  82. 10K Resources
  83. ^^^^^^^^^^^^^
  84. ========================= ============ ============ ============ ============ ================
  85. System PUT Store GET SPARQL Query Py-API retrieval
  86. ========================= ============ ============ ============ ============ ================
  87. FCREPO / Modeshape 4.7.5 49ms (100%) 3.7Gb (100%) 6.2s (100%) N/A N/A
  88. Lakesuperior 1.0a17 78ms (159%) 298Mb (8%) 2.8s 0m1.194s Not measured
  89. Lakesuperior 1.0a18 62ms (126%) 789Mb (21%) 2.2s 0m2.214s 66ms
  90. ========================= ============ ============ ============ ============ ================
  91. **Notes:**
  92. - The Python API time for the GET request in alpha18 is 8.5% of the request.
  93. This means that over 91% of the time is spent serializing the results.
  94. This time could be dramatically reduced by using faster serialization
  95. libraries, or can be outright zeroed out by an application that uses the
  96. Python API directly and manipulates the native RDFLib objects (of course, if
  97. a serialized output is eventually needed, that cost is unavoidable).
  98. - Similarly, the ``triples`` retrieval method of the SPARQL query only takes
  99. 13.6% of the request time. The rest is spent evaluating SPARQL and results.
  100. An application can use ``triples`` directly for relatively simple lookups
  101. without that overhead.
  102. 100K Resources
  103. ^^^^^^^^^^^^^^
  104. ========================= =============== ============= ============= =============== ============ ================
  105. System PUT POST Store GET Query Py-API retrieval
  106. ========================= =============== ============= ============= =============== ============ ================
  107. FCREPO / Modeshape 4.7.5 500ms* (100%) 38ms (100%) 13Gb (100%) 2m6.7s (100%) N/A N/A
  108. Lakesuperior 1.0a17 104ms (21%) 104ms (273%) 5.3Gb (40%) 0m17.0s (13%) 0m12.481s 3810ms
  109. Lakesuperior 1.0a18 79ms (15%) 79ms (207%) 7.5Gb (58%) 0m14.2s (11%) 0m4.214s** 905ms
  110. ========================= =============== ============= ============= =============== ============ ================
  111. \* POST was stopped at 50K resources. From looking at ingest timings over time
  112. we can easily infer that ingest time would further increase. This is the
  113. manifestation of the "many members" issue. The "Store" value is for the PUT
  114. operation which ran regularly with 100K resources.
  115. \*\* Timing based on a warm cache. The first query timed at 0m22.2s.
  116. .. _ole-workhorse-server-1:
  117. ‘Ole Workhorse’ server
  118. ~~~~~~~~~~~~~~~~~~~~~~
  119. 10K Resources
  120. ^^^^^^^^^^^^^
  121. ========================= ============== ============== ============== ============== ==================
  122. System PUT Store GET SPARQL Query Py-API retrieval
  123. ========================= ============== ============== ============== ============== ==================
  124. FCREPO / Modeshape 4.7.5 285ms (100%) 3.7Gb (100%) 9.6s (100%) N/A N/A
  125. Lakesuperior 1.0a17 446ms 298Mb 5.6s (58%) 0m1.194s Not measured
  126. Lakesuperior 1.0a18 Not measured Not measured Not measured Not measured Not measured
  127. ========================= ============== ============== ============== ============== ==================
  128. Conclusions
  129. -----------
  130. Lakesuperior appears to be markedly slower on writes and markedly faster
  131. on reads. Both these factors are very likely related to the underlying
  132. LMDB store which is optimized for read performance.
  133. In a real-world application scenario, in which a client may perform multiple
  134. reads before and after storing resources, the write performance gap may
  135. decrease. A Python application using the Python API for querying and writing
  136. would experience a dramatic improvement in reading timings, and somewhat in
  137. write timings.
  138. Comparison of results between the laptop and the server demonstrates
  139. that both read and write performance ratios between repository systems are
  140. identical in the two environments.
  141. As it may be obvious, these are only very partial and specific
  142. results. They should not be taken as a thorough performance assessment.
  143. Such an assessment may be impossible and pointless to make given the
  144. very different nature of the storage models, which may behave radically
  145. differently depending on many variables.