├── .gitignore ├── Jamroot ├── LICENSE_1_0.txt ├── README.md ├── benchmark_casablanca.cpp ├── benchmark_jsoncpp.cpp ├── benchmark_libjson.cpp ├── benchmark_qjsondoc.cpp ├── benchmark_rapidjson.cpp ├── benchmark_spirit.cpp ├── data ├── canada.json ├── one-json-per-line.jsons └── results.md ├── high_resolution_timer.hpp ├── json_benchmark.hpp └── run_benchmark.bat /.gitignore: -------------------------------------------------------------------------------- 1 | # Boost.Build 2 | bin 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | 9 | # Compiled Dynamic libraries 10 | *.so 11 | *.dylib 12 | 13 | # Compiled Static libraries 14 | *.lai 15 | *.la 16 | *.a 17 | -------------------------------------------------------------------------------- /Jamroot: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2013 Mateusz Loskot 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # (See accompanying file LICENSE_1_0.txt or copy 6 | # at http://www.boost.org/LICENSE_1_0.txt) 7 | # 8 | import notfile 9 | import os ; 10 | 11 | # Configure dependencies 12 | 13 | local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; 14 | if $(BOOST_ROOT) 15 | { 16 | use-project /boost : $(BOOST_ROOT) ; 17 | } 18 | 19 | local JSON_SPIRIT_ROOT = [ os.environ JSON_SPIRIT_ROOT ] ; 20 | if $(JSON_SPIRIT_ROOT) 21 | { 22 | use-project /json_spirit : $(JSON_SPIRIT_ROOT) ; 23 | } 24 | 25 | local RAPIDJSON_ROOT = [ os.environ RAPIDJSON_ROOT ] ; 26 | 27 | local QT_ROOT = [ os.environ QTDIR ] ; 28 | if $(QT_ROOT) 29 | { 30 | using qt5 : $(QT_ROOT) ; 31 | } 32 | 33 | # Declare targets for dependencies 34 | lib jsoncpplib : : jsoncpp : : /usr/include/jsoncpp ; 35 | 36 | 37 | # Declare project 38 | project json_benchmark 39 | : 40 | requirements 41 | . 42 | clang:-std=c++11 43 | gcc:-std=c++0x 44 | gcc:multi 45 | gcc:-ftemplate-depth=300 46 | msvc:/wd4996 47 | /boost//headers 48 | ; 49 | 50 | # Declare executable targets of benchmarking programs 51 | rule bexe ( sources + : requirements * : target-name ? : default-build * ) 52 | { 53 | target-name ?= $(sources[1]:D=:S=) ; 54 | exe $(target-name) : $(sources) : $(requirements) : $(default-build) ; 55 | } 56 | 57 | bexe benchmark_spirit.cpp /json_spirit//json ; 58 | bexe benchmark_rapidjson.cpp : $(RAPIDJSON_ROOT)/include ; 59 | bexe benchmark_jsoncpp.cpp jsoncpplib ; 60 | 61 | if $(QT_ROOT) 62 | { 63 | exe benchmark_qjsondoc : benchmark_qjsondoc.cpp /qt5//QtCore ; 64 | } 65 | 66 | # TODO: find out how to run all programs after build 67 | #actions run-benchmark { $(>[1]) ; } 68 | #notfile benchmark_rapidjson-run : @run-benchmark : benchmark_rapidjson ; 69 | 70 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JSON Benchmark 2 | ============== 3 | 4 | --------------------------------------- 5 | 6 | **NOTE: This benchmark is not actively maintained and has been superseded by Milo's [Native JSON Benchmark](https://github.com/miloyip/nativejson-benchmark).** 7 | 8 | --------------------------------------- 9 | 10 | 11 | Simple benchmark for a number of **C++** **JSON** libraries. 12 | 13 | List of currently measured libraries: 14 | 15 | * [casablanca](https://casablanca.codeplex.com/) - C++ REST SDK (codename "Casablanca") 16 | * [json_spirit](https://github.com/cierelabs/json_spirit) 17 | * [jsoncpp](http://jsoncpp.sourceforge.net/) 18 | * [libjson](http://sourceforge.net/projects/libjson/) 19 | * [rapidjson](http://code.google.com/p/rapidjson/) 20 | * [QJsonDocument](http://qt-project.org/doc/qt-5.0/qtcore/qjsondocument.html) 21 | 22 | Requirements 23 | * C++11 compiler 24 | * Boost headers 25 | * 26 | There are no C libraries included in this benchmark, but feel free if you'd like to 27 | contribute some (obviously, a C++ wrapper would be required). 28 | 29 | JSON Reading 30 | ============ 31 | 32 | Recorded are elapsed times of parsing/reading/consuming JSON string and 33 | constructing corresponding value object in memory of type as defined 34 | by interface of a particular library. 35 | 36 | Single mark of elapsed time is collected for ```I``` iterations of ```P``` parsings. 37 | Each mark is repeated ```M``` times in order to obtain lowest and highest timing. 38 | 39 | For example, if there are ```P``` JSON different strings loaded into test data container, 40 | and all strings are parsed ```I``` times, total elapsed time of all ```I x P``` 41 | parsings is measured; this is repeated ```M``` times. 42 | 43 | There are two sets of JSON test data: 44 | * **large** - single large JSON object 45 | * ```data/canada.json``` contains contour of Canada border in [GeoJSON](http://geojson.org) 46 | * **small** - collection of small JSON objects: 47 | * ```data/one-json-per-line.jsons``` collection of 500 sample JSON strings from sub-100 to 500 characters long. 48 | 49 | 50 | Important notes: 51 | * All timings obtained running optimised builds of the binaries. 52 | * All libraries are used to read and parse strings of narrow ```char``` 53 | characters (i.e. ```std::string```), no ```wchar_t``` strings are used. 54 | * rapidjson was used in DOM parsing mode, not SAX 55 | * libjson supports lazy parsing, times collected for this modes was: 56 | ``` 57 | libjson.small: 1000 iterations of 500 parsings in 0.478065 to 0.486636 sec based on 2 benchmarks 58 | ``` 59 | 60 | GCC 4.7 (64-bit build) 61 | ---------------------- 62 | 63 | HW: Intel(R) Xeon(R) CPU E5-2630L 0 @ 2.00GHz, 2 GB RAM; 64 | OS: Linux 64-bit (Debian 7) 65 | 66 | * small 67 | 68 | ``` 69 | rapidjson.small: 1000 iterations of 500 parsings in 1.08639 to 1.08639 sec based on 2 benchmarks 70 | jsoncpp.small: 1000 iterations of 500 parsings in 18.5563 to 19.6208 sec based on 2 benchmarks 71 | spirit.small: 1000 iterations of 500 parsings in 27.4479 to 27.4479 sec based on 2 benchmarks 72 | ``` 73 | 74 | * large 75 | 76 | ``` 77 | rapidjson.large: 1000 iterations of 1 parsings in 13.8156 to 14.2241 sec based on 2 benchmarks 78 | jsoncpp.large: 1000 iterations of 1 parsings in 376.313 to 384.015 sec based on 2 benchmarks 79 | spirit.large: 1000 iterations of 1 parsings in 988.167 to 995.634 sec based on 2 benchmarks 80 | ``` 81 | 82 | Visual C++ 11.0 (32-bit build) 83 | ------------------------------ 84 | 85 | HW: Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz, 16 GB RAM; 86 | OS: Windows 7 64-bit 87 | 88 | * small 89 | 90 | ``` 91 | rapidjson.small: 1000 iterations of 500 parsings in 0.961858 to 0.962361 sec based on 2 benchmarks 92 | libjson.small: 1000 iterations of 500 parsings in 5.2787 to 5.29663 sec based on 2 benchmarks 93 | jsoncpp.small: 1000 iterations of 500 parsings in 5.762 to 5.77978 sec based on 2 benchmarks 94 | casablanca.small: 1000 iterations of 500 parsings in 17.7374 to 17.7587 sec based on 2 benchmarks 95 | spirit.small: 1000 iterations of 500 parsings in 22.137 to 22.137 sec based on 2 benchmarks 96 | ``` 97 | 98 | * large 99 | 100 | ``` 101 | rapidjson.large: 1000 iterations of 1 parsings in 7.96621 to 7.97555 sec based on 2 benchmarks 102 | libjson.large: 1000 iterations of 1 parsings in 240.289 to 240.289 sec based on 2 benchmarks 103 | ``` 104 | 105 | JSON Writing 106 | ============ 107 | 108 | *TODO* 109 | 110 | Building 111 | ======== 112 | 113 | [Boost.Build](http://www.boost.org/boost-build2/) configuration is provided and 114 | it assumes either your ```user-config.jam``` file has ```use-project``` entry for 115 | [Boost](http://boost.org) (headers are required) and for every JSON library. For example: 116 | 117 | ``` 118 | use-project /boost : /home/mloskot/boost/trunk ; 119 | use-project /json_spirit : /home/mloskot/json_spirit ; 120 | ``` 121 | 122 | Alternatively, set the environment variables: 123 | * ```BOOST_ROOT``` for Boost 124 | * ```_ROOT``` for each library. 125 | 126 | The environment variables can be passed directly on the Boost.Build invocation: 127 | ``` 128 | b2 -sBOOST_ROOT=/home/mloskot/boost/trunk \ 129 | -sJSON_SPIRIT_ROOT=/home/mloskot/json_spirit \ 130 | -sRAPIDJSON_ROOT=/home/mloskot/rapidjson 131 | ``` 132 | and so on. 133 | 134 | Or, simply edit ```Jamroot``` file. 135 | 136 | In order to build the benchmark programs run: 137 | 138 | ``` 139 | b2 variant=release 140 | ``` 141 | 142 | Other Benchmarks 143 | ================ 144 | 145 | List of other benchmarks of JSON C++ libraries available on the Web: 146 | 147 | * [Native JSON Benchmark](https://github.com/miloyip/nativejson-benchmark) by Milo Yip (2015) 148 | * [Thiago Macieira's benchmark](https://plus.google.com/108138837678270193032/posts/7EVTACgwtxK) 149 | 150 | Disclaimer 151 | ========== 152 | 153 | I created this benchmark driven by curiosity and for my own purposes, with hope to 154 | obtain useful and interesting results, for myself and others too. 155 | I do not have any objective of making ultimate performance shootout. 156 | This is not a rocket science, but a simple set of C++ programs, with likelyhood 157 | of bugs or inconsistencies. Found any, please report. Comments and improvements 158 | are always welcome! 159 | 160 | License 161 | ======= 162 | 163 | Distributed under the Boost Software License, Version 1.0. 164 | See accompanying file LICENSE_1_0.txt or copy at 165 | http://www.boost.org/LICENSE_1_0.txt. 166 | 167 | Copyright 168 | ========= 169 | 170 | Copyright 2013 Mateusz Loskot 171 | 172 | and other hackers (see copyright headers in source files). 173 | -------------------------------------------------------------------------------- /benchmark_casablanca.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Mateusz Loskot 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // (See accompanying file LICENSE_1_0.txt or copy at 6 | // http://www.boost.org/LICENSE_1_0.txt) 7 | // 8 | #include "json_benchmark.hpp" 9 | #include // casablanca 10 | namespace jb = jsonbench; 11 | namespace wj = web::json; 12 | int main() 13 | { 14 | try 15 | { 16 | //auto const jsons = jb::get_one_json_per_line(); 17 | auto const jsons = jb::get_json(); 18 | auto const marks = jb::benchmark(jsons, [](std::string const& s) { 19 | std::stringstream ss; 20 | ss << s; 21 | wj::value jv = wj::value::parse(ss); 22 | #ifdef JSON_BENCHMARK_DUMP_PARSED_JSON 23 | jv.serialize(std::cout); 24 | #endif 25 | return !jv.is_null(); 26 | }); 27 | 28 | jb::print_result(std::cout << "casablanca: ", marks); 29 | return EXIT_SUCCESS; 30 | } 31 | catch (std::exception const& e) 32 | { 33 | std::cerr << e.what() < 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // (See accompanying file LICENSE_1_0.txt or copy at 6 | // http://www.boost.org/LICENSE_1_0.txt) 7 | // 8 | #include "json_benchmark.hpp" 9 | #include // jsoncpp 10 | int main() 11 | { 12 | try 13 | { 14 | jsonbench::run_benchmark("jsoncpp", [](std::string const& s) { 15 | Json::Value jv; 16 | Json::Reader jr; 17 | #ifdef JSON_BENCHMARK_DUMP_PARSED_JSON 18 | Json::FastWriter jw; 19 | std::cout << jw.write(jv) << std::endl; 20 | #endif 21 | return jr.parse(s, jv) && jv.type() != Json::nullValue; 22 | }); 23 | 24 | return EXIT_SUCCESS; 25 | } 26 | catch (std::exception const& e) 27 | { 28 | std::cerr << e.what() < 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // (See accompanying file LICENSE_1_0.txt or copy at 6 | // http://www.boost.org/LICENSE_1_0.txt) 7 | // 8 | #include "json_benchmark.hpp" 9 | #include 10 | namespace jb = jsonbench; 11 | int main() 12 | { 13 | try 14 | { 15 | //auto const jsons = jb::get_one_json_per_line(); 16 | auto const jsons = jb::get_json(); 17 | auto const marks = jb::benchmark(jsons, [](std::string const& s) -> bool { 18 | auto jv = libjson::parse(s.c_str()); 19 | #ifdef JSON_BENCHMARK_DUMP_PARSED_JSON 20 | std::cout << jv.write() << std::endl; 21 | #endif 22 | return jv.type() != JSON_NULL; 23 | }); 24 | 25 | jb::print_result(std::cout << "libjson: ", marks); 26 | return EXIT_SUCCESS; 27 | } 28 | catch (std::exception const& e) 29 | { 30 | std::cerr << e.what() < 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // (See accompanying file LICENSE_1_0.txt or copy at 6 | // http://www.boost.org/LICENSE_1_0.txt) 7 | // 8 | 9 | 10 | #include "json_benchmark.hpp" 11 | 12 | #include 13 | 14 | 15 | int main() { 16 | try 17 | { 18 | jsonbench::run_benchmark("QJsonDocument", [](std::string const& s) { 19 | QJsonParseError err; 20 | return !QJsonDocument::fromJson(s.c_str(), &err).isNull() && (err.error == QJsonParseError::NoError); 21 | }); 22 | 23 | return EXIT_SUCCESS; 24 | } 25 | catch (std::exception const& e) 26 | { 27 | std::cerr << e.what() < 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // (See accompanying file LICENSE_1_0.txt or copy at 6 | // http://www.boost.org/LICENSE_1_0.txt) 7 | // 8 | #include "json_benchmark.hpp" 9 | #include 10 | #include 11 | #include 12 | #include 13 | namespace rj = rapidjson; 14 | int main() 15 | { 16 | try 17 | { 18 | jsonbench::run_benchmark("rapidjson", [](std::string const& s) { 19 | rj::Document d; 20 | d.Parse<0>(s.c_str()); 21 | #ifdef JSON_BENCHMARK_DUMP_PARSED_JSON 22 | rj::FileStream f(stdout); 23 | rj::PrettyWriter writer(f); 24 | d.Accept(writer); 25 | #endif 26 | return !d.IsNull(); 27 | }); 28 | 29 | return EXIT_SUCCESS; 30 | } 31 | catch (std::exception const& e) 32 | { 33 | std::cerr << e.what() < 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // (See accompanying file LICENSE_1_0.txt or copy at 6 | // http://www.boost.org/LICENSE_1_0.txt) 7 | // 8 | #include "json_benchmark.hpp" 9 | #include 10 | #include 11 | namespace cj = ciere::json; 12 | int main() 13 | { 14 | try 15 | { 16 | jsonbench::run_benchmark("spirit", [](std::string const& s) { 17 | cj::value jv; 18 | bool const parsed = cj::construct(s, jv); 19 | #ifdef JSON_BENCHMARK_DUMP_PARSED_JSON 20 | std::cout << jv << std::endl; 21 | #endif 22 | return parsed && jv.type() != cj::null_type; 23 | }); 24 | 25 | return EXIT_SUCCESS; 26 | } 27 | catch (std::exception const& e) 28 | { 29 | std::cerr << e.what() < 11 | #include 12 | 13 | #if defined(BOOST_HAS_UNISTD_H) 14 | #include 15 | #endif 16 | #include 17 | #include 18 | #include 19 | 20 | #if defined(BOOST_WINDOWS) 21 | 22 | #include 23 | 24 | namespace util 25 | { 26 | /////////////////////////////////////////////////////////////////////////////// 27 | // 28 | // high_resolution_timer 29 | // A timer object measures elapsed time. 30 | // CAUTION: Windows only! 31 | // 32 | /////////////////////////////////////////////////////////////////////////////// 33 | class high_resolution_timer 34 | { 35 | public: 36 | high_resolution_timer() 37 | { 38 | restart(); 39 | } 40 | 41 | high_resolution_timer(double t) 42 | { 43 | LARGE_INTEGER frequency; 44 | if (!QueryPerformanceFrequency(&frequency)) 45 | boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); 46 | 47 | start_time.QuadPart = (LONGLONG)(t * frequency.QuadPart); 48 | } 49 | 50 | high_resolution_timer(high_resolution_timer const& rhs) 51 | : start_time(rhs.start_time) 52 | { 53 | } 54 | 55 | static double now() 56 | { 57 | SYSTEMTIME st; 58 | GetSystemTime(&st); 59 | 60 | FILETIME ft; 61 | SystemTimeToFileTime(&st, &ft); 62 | 63 | LARGE_INTEGER now; 64 | now.LowPart = ft.dwLowDateTime; 65 | now.HighPart = ft.dwHighDateTime; 66 | 67 | // FileTime is in 100ns increments, result needs to be in [s] 68 | return now.QuadPart * 1e-7; 69 | } 70 | 71 | void restart() 72 | { 73 | if (!QueryPerformanceCounter(&start_time)) 74 | boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); 75 | } 76 | double elapsed() const // return elapsed time in seconds 77 | { 78 | LARGE_INTEGER now; 79 | if (!QueryPerformanceCounter(&now)) 80 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 81 | 82 | LARGE_INTEGER frequency; 83 | if (!QueryPerformanceFrequency(&frequency)) 84 | boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); 85 | 86 | return double(now.QuadPart - start_time.QuadPart) / frequency.QuadPart; 87 | } 88 | 89 | double elapsed_max() const // return estimated maximum value for elapsed() 90 | { 91 | LARGE_INTEGER frequency; 92 | if (!QueryPerformanceFrequency(&frequency)) 93 | boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); 94 | 95 | return double((std::numeric_limits::max)() - start_time.QuadPart) / 96 | double(frequency.QuadPart); 97 | } 98 | 99 | double elapsed_min() const // return minimum value for elapsed() 100 | { 101 | LARGE_INTEGER frequency; 102 | if (!QueryPerformanceFrequency(&frequency)) 103 | boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); 104 | 105 | return 1.0 / frequency.QuadPart; 106 | } 107 | 108 | private: 109 | LARGE_INTEGER start_time; 110 | }; 111 | 112 | } // namespace util 113 | 114 | #elif defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(_POSIX_THREAD_CPUTIME) 115 | 116 | #if _POSIX_THREAD_CPUTIME > 0 // timer always supported 117 | 118 | namespace util 119 | { 120 | 121 | /////////////////////////////////////////////////////////////////////////////// 122 | // 123 | // high_resolution_timer 124 | // A timer object measures elapsed time. 125 | // 126 | /////////////////////////////////////////////////////////////////////////////// 127 | class high_resolution_timer 128 | { 129 | public: 130 | high_resolution_timer() 131 | { 132 | start_time.tv_sec = 0; 133 | start_time.tv_nsec = 0; 134 | 135 | restart(); 136 | } 137 | 138 | high_resolution_timer(double t) 139 | { 140 | start_time.tv_sec = time_t(t); 141 | start_time.tv_nsec = (t - start_time.tv_sec) * 1e9; 142 | } 143 | 144 | high_resolution_timer(high_resolution_timer const& rhs) 145 | : start_time(rhs.start_time) 146 | { 147 | } 148 | 149 | static double now() 150 | { 151 | timespec now; 152 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) 153 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 154 | return double(now.tv_sec) + double(now.tv_nsec) * 1e-9; 155 | } 156 | 157 | void restart() 158 | { 159 | if (-1 == clock_gettime(CLOCK_REALTIME, &start_time)) 160 | boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); 161 | } 162 | double elapsed() const // return elapsed time in seconds 163 | { 164 | timespec now; 165 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) 166 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 167 | 168 | if (now.tv_sec == start_time.tv_sec) 169 | return double(now.tv_nsec - start_time.tv_nsec) * 1e-9; 170 | 171 | return double(now.tv_sec - start_time.tv_sec) + 172 | (double(now.tv_nsec - start_time.tv_nsec) * 1e-9); 173 | } 174 | 175 | double elapsed_max() const // return estimated maximum value for elapsed() 176 | { 177 | return double((std::numeric_limits::max)() - start_time.tv_sec); 178 | } 179 | 180 | double elapsed_min() const // return minimum value for elapsed() 181 | { 182 | timespec resolution; 183 | if (-1 == clock_getres(CLOCK_REALTIME, &resolution)) 184 | boost::throw_exception(std::runtime_error("Couldn't get resolution")); 185 | return double(resolution.tv_sec + resolution.tv_nsec * 1e-9); 186 | } 187 | 188 | private: 189 | timespec start_time; 190 | }; 191 | 192 | } // namespace util 193 | 194 | #else // _POSIX_THREAD_CPUTIME > 0 195 | 196 | #include 197 | 198 | // availability of high performance timers must be checked at runtime 199 | namespace util 200 | { 201 | /////////////////////////////////////////////////////////////////////////////// 202 | // 203 | // high_resolution_timer 204 | // A timer object measures elapsed time. 205 | // 206 | /////////////////////////////////////////////////////////////////////////////// 207 | class high_resolution_timer 208 | { 209 | public: 210 | high_resolution_timer() 211 | : use_backup(sysconf(_SC_THREAD_CPUTIME) <= 0) 212 | { 213 | if (!use_backup) { 214 | start_time.tv_sec = 0; 215 | start_time.tv_nsec = 0; 216 | } 217 | restart(); 218 | } 219 | 220 | high_resolution_timer(double t) 221 | : use_backup(sysconf(_SC_THREAD_CPUTIME) <= 0) 222 | { 223 | if (!use_backup) { 224 | start_time.tv_sec = time_t(t); 225 | start_time.tv_nsec = (t - start_time.tv_sec) * 1e9; 226 | } 227 | } 228 | 229 | high_resolution_timer(high_resolution_timer const& rhs) 230 | : use_backup(sysconf(_SC_THREAD_CPUTIME) <= 0), 231 | start_time(rhs.start_time) 232 | { 233 | } 234 | 235 | static double now() 236 | { 237 | if (sysconf(_SC_THREAD_CPUTIME) <= 0) 238 | return double(std::clock()); 239 | 240 | timespec now; 241 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) 242 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 243 | return double(now.tv_sec) + double(now.tv_nsec) * 1e-9; 244 | } 245 | 246 | void restart() 247 | { 248 | if (use_backup) 249 | start_time_backup.restart(); 250 | else if (-1 == clock_gettime(CLOCK_REALTIME, &start_time)) 251 | boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); 252 | } 253 | double elapsed() const // return elapsed time in seconds 254 | { 255 | if (use_backup) 256 | return start_time_backup.elapsed(); 257 | 258 | timespec now; 259 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) 260 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 261 | 262 | if (now.tv_sec == start_time.tv_sec) 263 | return double(now.tv_nsec - start_time.tv_nsec) * 1e-9; 264 | 265 | return double(now.tv_sec - start_time.tv_sec) + 266 | (double(now.tv_nsec - start_time.tv_nsec) * 1e-9); 267 | } 268 | 269 | double elapsed_max() const // return estimated maximum value for elapsed() 270 | { 271 | if (use_backup) 272 | start_time_backup.elapsed_max(); 273 | 274 | return double((std::numeric_limits::max)() - start_time.tv_sec); 275 | } 276 | 277 | double elapsed_min() const // return minimum value for elapsed() 278 | { 279 | if (use_backup) 280 | start_time_backup.elapsed_min(); 281 | 282 | timespec resolution; 283 | if (-1 == clock_getres(CLOCK_REALTIME, &resolution)) 284 | boost::throw_exception(std::runtime_error("Couldn't get resolution")); 285 | return double(resolution.tv_sec + resolution.tv_nsec * 1e-9); 286 | } 287 | 288 | private: 289 | bool use_backup; 290 | timespec start_time; 291 | boost::timer start_time_backup; 292 | }; 293 | 294 | } // namespace util 295 | 296 | #endif // _POSIX_THREAD_CPUTIME > 0 297 | 298 | #else // !defined(BOOST_WINDOWS) && (!defined(_POSIX_TIMERS) 299 | // || _POSIX_TIMERS <= 0 300 | // || !defined(_POSIX_THREAD_CPUTIME) 301 | // || _POSIX_THREAD_CPUTIME <= 0) 302 | 303 | #if defined(BOOST_HAS_GETTIMEOFDAY) 304 | 305 | // For platforms that do not support _POSIX_TIMERS but do have 306 | // GETTIMEOFDAY, which is still preferable to std::clock() 307 | #include 308 | 309 | namespace util 310 | { 311 | 312 | /////////////////////////////////////////////////////////////////////////// 313 | // 314 | // high_resolution_timer 315 | // A timer object measures elapsed time. 316 | // 317 | // Implemented with gettimeofday() for platforms that support it, 318 | // such as Darwin (OS X) but do not support the previous options. 319 | // 320 | // Copyright (c) 2009 Edward Grace 321 | // 322 | /////////////////////////////////////////////////////////////////////////// 323 | class high_resolution_timer 324 | { 325 | private: 326 | template 327 | static inline double unsigned_diff(const U &a, const U &b) 328 | { 329 | if (a > b) 330 | return static_cast(a-b); 331 | return -static_cast(b-a); 332 | } 333 | 334 | // @brief Return the difference between two timeval types. 335 | // 336 | // @param t1 The most recent timeval. 337 | // @param t0 The historical timeval. 338 | // 339 | // @return The difference between the two in seconds. 340 | double elapsed(const timeval &t1, const timeval &t0) const 341 | { 342 | if (t1.tv_sec == t0.tv_sec) 343 | return unsigned_diff(t1.tv_usec,t0.tv_usec) * 1e-6; 344 | 345 | // We do it this way as the result of the difference of the 346 | // microseconds can be negative if the clock is implemented so 347 | // that the seconds timer increases in large steps. 348 | // 349 | // Naive subtraction of the unsigned types and conversion to 350 | // double can wreak havoc! 351 | return unsigned_diff(t1.tv_sec,t0.tv_sec) + 352 | unsigned_diff(t1.tv_usec,t0.tv_usec) * 1e-6; 353 | } 354 | 355 | public: 356 | high_resolution_timer() 357 | { 358 | start_time.tv_sec = 0; 359 | start_time.tv_usec = 0; 360 | 361 | restart(); 362 | } 363 | 364 | high_resolution_timer(double t) 365 | { 366 | start_time.tv_sec = time_t(t); 367 | start_time.tv_usec = (t - start_time.tv_sec) * 1e6; 368 | } 369 | 370 | high_resolution_timer(high_resolution_timer const& rhs) 371 | : start_time(rhs.start_time) 372 | { 373 | } 374 | 375 | static double now() 376 | { 377 | // Under some implementations gettimeofday() will always 378 | // return zero. If it returns anything else however then 379 | // we accept this as evidence of an error. Note we are 380 | // not assuming that -1 explicitly indicates the error 381 | // condition, just that non zero is indicative of the 382 | // error. 383 | timeval now; 384 | if (gettimeofday(&now, NULL)) 385 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 386 | return double(now.tv_sec) + double(now.tv_usec) * 1e-6; 387 | } 388 | 389 | void restart() 390 | { 391 | if (gettimeofday(&start_time, NULL)) 392 | boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); 393 | } 394 | 395 | double elapsed() const // return elapsed time in seconds 396 | { 397 | timeval now; 398 | if (gettimeofday(&now, NULL)) 399 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 400 | return elapsed(now,start_time); 401 | } 402 | 403 | double elapsed_max() const // return estimated maximum value for elapsed() 404 | { 405 | return double((std::numeric_limits::max)() - start_time.tv_sec); 406 | } 407 | 408 | double elapsed_min() const // return minimum value for elapsed() 409 | { 410 | // On systems without an explicit clock_getres or similar 411 | // we can only estimate an upper bound on the resolution 412 | // by repeatedly calling the gettimeofday function. This 413 | // is often likely to be indicative of the true 414 | // resolution. 415 | timeval t0, t1; 416 | double delta(0); 417 | 418 | if (gettimeofday(&t0, NULL)) 419 | boost::throw_exception(std::runtime_error("Couldn't get resolution.")); 420 | 421 | // Spin around in a tight loop until we observe a change 422 | // in the reported timer value. 423 | do { 424 | if (gettimeofday(&t1, NULL)) 425 | boost::throw_exception(std::runtime_error("Couldn't get resolution.")); 426 | delta = elapsed(t1, t0); 427 | } while (delta <= 0.0); 428 | 429 | return delta; 430 | } 431 | 432 | private: 433 | timeval start_time; 434 | }; 435 | 436 | } 437 | 438 | #else // BOOST_HAS_GETTIMEOFDAY 439 | 440 | // For platforms other than Windows or Linux, or not implementing gettimeofday 441 | // simply fall back to boost::timer 442 | #include 443 | 444 | namespace util 445 | { 446 | struct high_resolution_timer 447 | : boost::timer 448 | { 449 | static double now() 450 | { 451 | return double(std::clock()); 452 | } 453 | }; 454 | } 455 | 456 | #endif 457 | 458 | #endif 459 | 460 | #endif // HIGH_RESOLUTION_TIMER_AUG_14_2009_0425PM 461 | 462 | // 463 | // $Log: high_resolution_timer.hpp,v $ 464 | // Revision 1.4 2009/08/14 15:28:10 graceej 465 | // * It is entirely possible for the updating clock to increment the 466 | // * seconds and *decrement* the microseconds field. Consequently 467 | // * when subtracting these unsigned microseconds fields a wrap-around 468 | // * error can occur. For this reason elapsed(t1, t0) is used in a 469 | // * similar maner to cycle.h this preserves the sign of the 470 | // * difference. 471 | // 472 | 473 | 474 | -------------------------------------------------------------------------------- /json_benchmark.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Mateusz Loskot 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // (See accompanying file LICENSE_1_0.txt or copy 6 | // at http://www.boost.org/LICENSE_1_0.txt) 7 | // 8 | #ifndef MLOSKOT_JSON_BENCHMARK_HPP_INCLUDED 9 | #define MLOSKOT_JSON_BENCHMARK_HPP_INCLUDED 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "high_resolution_timer.hpp" 21 | 22 | // Uncomment to print all tested JSON data to stdout 23 | #ifndef JSON_BENCHMARK_DUMP_PARSED_JSON 24 | //#define JSON_BENCHMARK_DUMP_PARSED_JSON 25 | #endif 26 | 27 | namespace jsonbench 28 | { 29 | 30 | // 31 | // Default benchmark settings 32 | // 33 | std::size_t max_marks = 2; // 10; 34 | std::size_t max_iterations = 1000; 35 | 36 | // 37 | // Routines handling test data files 38 | // 39 | typedef std::vector jsons_t; 40 | 41 | inline jsons_t load_json(std::string file) 42 | { 43 | typedef std::string::value_type char_t; 44 | typedef std::istreambuf_iterator iterator_t; 45 | 46 | jsons_t v; 47 | std::ifstream ifs(file); 48 | v.push_back(std::string(iterator_t(ifs), (iterator_t()))); 49 | return v; 50 | } 51 | 52 | inline jsons_t load_jsons(std::string file) 53 | { 54 | jsons_t v; 55 | std::ifstream ifs(file); 56 | for (std::string line; std::getline(ifs, line); ) 57 | v.push_back(line); 58 | return v; 59 | } 60 | 61 | // load single large JSON string 62 | inline jsons_t get_large() 63 | { 64 | return load_json("data/canada.json"); 65 | } 66 | 67 | // load collection of small to medium size JSON strings 68 | inline jsons_t get_small() 69 | { 70 | return load_jsons("data/one-json-per-line.jsons"); 71 | } 72 | 73 | // 74 | // Benchmark running routines 75 | // 76 | typedef std::tuple result_t; 77 | 78 | template 79 | inline std::ostream& print_result(std::ostream& os, Result r) 80 | { 81 | using std::get; 82 | os << get<1>(r) << " iterations of " << get<2>(r) << " parsings in " 83 | << get<3>(r) << " to " << get<4>(r) << " sec based on " 84 | << get<0>(r) << " benchmarks" << std::endl; 85 | return os; 86 | } 87 | 88 | template 89 | inline void set_mark(Result& r, Timer const& t) 90 | { 91 | using std::get; 92 | auto const m = t.elapsed(); 93 | get<3>(r) = get<3>(r) < 0 ? m : (std::min)(m, get<3>(r)); 94 | get<4>(r) = get<4>(r) < 0 ? m : (std::max)(m, get<4>(r)); 95 | } 96 | 97 | template 98 | inline result_t benchmark(std::size_t marks, std::size_t iterations, Container const& jsons, Parse parse) 99 | { 100 | result_t r = std::make_tuple(marks, iterations, jsons.size(), -1.0, -1.0); 101 | 102 | for (decltype(marks) m = 0; m < marks; ++m) 103 | { 104 | util::high_resolution_timer t; 105 | for (decltype(iterations) i = 0U; i < iterations; ++i) 106 | { 107 | for (auto const& s : jsons) 108 | { 109 | if (!parse(s)) 110 | throw std::runtime_error("parse failed"); 111 | } 112 | } 113 | set_mark(r, t); 114 | } 115 | return r; 116 | } 117 | 118 | template 119 | inline result_t benchmark(Container const& jsons, Parse parse) 120 | { 121 | return benchmark(max_marks, max_iterations, jsons, parse); 122 | } 123 | 124 | template 125 | inline void run_benchmark(char const* name, Parse parse) 126 | { 127 | { 128 | auto const marks = benchmark(max_marks, max_iterations, get_small(), parse); 129 | print_result(std::cout << name << ".small: ", marks); 130 | } 131 | { 132 | auto const marks = benchmark(max_marks, max_iterations, get_large(), parse); 133 | print_result(std::cout << name << ".large: ", marks); 134 | } 135 | } 136 | 137 | } // namespace jsonbench 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /run_benchmark.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | for /r %%f in (%1\*.exe) do %%f --------------------------------------------------------------------------------