Unverified Commit be4fba7b authored by Niels Lohmann's avatar Niels Lohmann

Merge branch 'develop' of https://github.com/nlohmann/json into develop

parents f193427e 30946404
json_unit
json_benchmarks
json_benchmarks_simple
fuzz-testing
*.dSYM
......
all: json_benchmarks
./json_benchmarks
json_benchmarks: src/benchmarks.cpp ../src/json.hpp number_jsons
#
# Build/run json.hpp benchmarks, eg. CXX=g++-7 make
#
# The existing json_benchmarks did not allow optimization under some compilers
#
all: json_benchmarks json_benchmarks_simple number_jsons
bash -c 'time ./json_benchmarks'
bash -c 'time ./json_benchmarks_simple'
json_benchmarks: src/benchmarks.cpp ../src/json.hpp
$(CXX) -std=c++11 -pthread $(CXXFLAGS) -DNDEBUG -O3 -flto -I thirdparty/benchpress -I thirdparty/cxxopts -I../src src/benchmarks.cpp $(LDFLAGS) -o $@
json_benchmarks_simple: src/benchmarks_simple.cpp ../src/json.hpp
$(CXX) -std=c++11 $(CXXFLAGS) -DNDEBUG -O3 -flto -I../src $(<) $(LDFLAGS) -o $@
number_jsons:
(test -e files/numbers/floats.json -a -e files/numbers/signed_ints.json -a -e files/numbers/unsigned_ints.json) || (cd files/numbers ; python generate.py)
clean:
rm -f json_benchmarks files/numbers/*.json
rm -f json_benchmarks json_benchmarks_simple files/numbers/*.json
......@@ -34,6 +34,19 @@ static void bench(benchpress::context& ctx,
{
// using string streams for benchmarking to factor-out cold-cache disk
// access.
#if defined( FROMFILE )
std::ifstream istr;
{
istr.open( in_path, std::ifstream::in );
// read the stream once
json j;
istr >> j;
// clear flags and rewind
istr.clear();
istr.seekg(0);
}
#else
std::stringstream istr;
{
// read file into string stream
......@@ -43,11 +56,12 @@ static void bench(benchpress::context& ctx,
// read the stream once
json j;
j << istr;
istr >> j;
// clear flags and rewind
istr.clear();
istr.seekg(0);
}
#endif
switch (mode)
{
......@@ -62,7 +76,7 @@ static void bench(benchpress::context& ctx,
istr.clear();
istr.seekg(0);
json j;
j << istr;
istr >> j;
}
break;
......@@ -74,7 +88,7 @@ static void bench(benchpress::context& ctx,
{
// create JSON value from input
json j;
j << istr;
istr >> j;
std::stringstream ostr;
ctx.reset_timer();
......
//
// benchmarks_simple.cpp -- a less complex version of benchmarks.cpp, that better reflects actual performance
//
// For some reason, the complexity of benchmarks.cpp doesn't allow
// the compiler to optimize code using json.hpp effectively. The
// exact same tests, with the use of benchpress and cxxopts produces
// much faster code, at least under g++.
//
#include <fstream>
#include <iostream>
#include <chrono>
#include <list>
#include <tuple>
#include <json.hpp>
using json = nlohmann::json;
enum class EMode { input, output, indent };
static double bench(const EMode mode, size_t iters, const std::string& in_path )
{
// using string streams for benchmarking to factor-out cold-cache disk
// access. Define FROMFILE to use file I/O instead.
#if defined( FROMFILE )
std::ifstream istr;
{
istr.open( in_path, std::ifstream::in );
// read the stream once
json j;
istr >> j;
// clear flags and rewind
istr.clear();
istr.seekg(0);
}
#else
std::stringstream istr;
{
// read file into string stream
std::ifstream input_file(in_path);
istr << input_file.rdbuf();
input_file.close();
// read the stream once
json j;
istr >> j;
// clear flags and rewind
istr.clear();
istr.seekg(0);
}
#endif
double tps = 0;
switch (mode)
{
// benchmarking input
case EMode::input:
{
auto start = std::chrono::system_clock::now();
for (size_t i = 0; i < iters; ++i)
{
// clear flags and rewind
istr.clear();
istr.seekg(0);
json j;
istr >> j;
}
auto ended = std::chrono::system_clock::now();
tps = 1.0 / std::chrono::duration<double>( ended - start ).count();
break;
}
// benchmarking output
case EMode::output:
case EMode::indent:
{
// create JSON value from input
json j;
istr >> j;
std::stringstream ostr;
auto start = std::chrono::system_clock::now();
for (size_t i = 0; i < iters; ++i)
{
if (mode == EMode::indent)
{
ostr << j;
}
else
{
ostr << std::setw(4) << j;
}
// reset data
ostr.str(std::string());
}
auto ended = std::chrono::system_clock::now();
tps = 1.0 / std::chrono::duration<double>( ended - start ).count();
break;
}
}
return tps;
}
template <typename T>
struct average {
T _sum { 0 };
size_t _count { 0 };
T operator+=( const T &val_ ) { _sum += val_; +_count++; return val_; }
operator T() { return _sum / _count; }
};
// Execute each test approximately enough times to get near 1
// transaction per second, and compute the average; a single aggregate
// number that gives a performance metric representing both parsing
// and output.
int main( int, char ** )
{
std::list<std::tuple<std::string, EMode, size_t, std::string>> tests {
{ "parse jeopardy.json", EMode::input, 2, "files/jeopardy/jeopardy.json" },
{ "parse canada.json", EMode::input, 30, "files/nativejson-benchmark/canada.json" },
{ "parse citm_catalog.json", EMode::input, 120, "files/nativejson-benchmark/citm_catalog.json" },
{ "parse twitter.json", EMode::input, 225, "files/nativejson-benchmark/twitter.json" },
{ "parse floats.json", EMode::input, 5, "files/numbers/floats.json" },
{ "parse signed_ints.json", EMode::input, 6, "files/numbers/signed_ints.json" },
{ "parse unsigned_ints.json", EMode::input, 6, "files/numbers/unsigned_ints.json" },
{ "dump jeopardy.json", EMode::output, 5, "files/jeopardy/jeopardy.json" },
{ "dump jeopardy.json w/ind.", EMode::indent, 5, "files/jeopardy/jeopardy.json" },
{ "dump floats.json", EMode::output, 2, "files/numbers/floats.json" },
{ "dump signed_ints.json", EMode::output, 20, "files/numbers/signed_ints.json" },
};
average<double> avg;
for ( auto t : tests ) {
std::string name, path;
EMode mode;
size_t iters;
std::tie(name, mode, iters, path) = t;
auto tps = bench( mode, iters, path );
avg += tps;
std::cout
<< std::left
<< std::setw( 30 ) << name
<< std::right
<< " x " << std::setw( 3 ) << iters
<< std::left
<< " == " << std::setw( 10 ) << tps
<< std::right
<< " TPS, " << std::setw( 8 ) << std::round( tps * 1e6 / iters )
<< " ms/op"
<< std::endl;
}
std::cout << std::setw( 40 ) << "" << std::string( 10, '-' ) << std::endl;
std::cout << std::setw( 40 ) << "" << std::setw( 10 ) << std::left << avg << " TPS Average" << std::endl;
return 0;
}
This diff is collapsed.
......@@ -66,6 +66,22 @@ set_target_properties(catch_main PROPERTIES
)
target_include_directories(catch_main PRIVATE "thirdparty/catch")
# https://stackoverflow.com/questions/2368811/how-to-set-warning-level-in-cmake
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
# Disable warning C4389: '==': signed/unsigned mismatch
# Disable warning C4309: 'static_cast': truncation of constant value
# Disable warning C4566: character represented by universal-character-name '\uFF01' cannot be represented in the current code page (1252)
# Disable warning C4996: 'nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::operator <<': was declared deprecated
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4389 /wd4309 /wd4566 /wd4996")
endif()
#############################################################################
# one executable for each unit test file
#############################################################################
......
......@@ -215,7 +215,7 @@ TEST_CASE("parser class")
std::string s = "\"1\"";
s[1] = '\0';
CHECK_THROWS_AS(json::parse(s.begin(), s.end()), json::parse_error&);
CHECK_THROWS_WITH(json::parse(s.begin(), s.end()), "[json.exception.parse_error.101] parse error at 2: syntax error - invalid string: control character must be escaped; last read: '\"'");
CHECK_THROWS_WITH(json::parse(s.begin(), s.end()), "[json.exception.parse_error.101] parse error at 2: syntax error - invalid string: control character must be escaped; last read: '\"<U+0000>'");
}
}
......
......@@ -247,7 +247,7 @@ TEST_CASE("constructors")
SECTION("std::pair")
{
std::pair<float, std::string> p{1.0, "string"};
std::pair<float, std::string> p{1.0f, "string"};
json j(p);
CHECK(j.type() == json::value_t::array);
......
......@@ -38,6 +38,11 @@ using nlohmann::json;
#include <unordered_set>
#include <iostream>
#if defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable : 4189) // local variable is initialized but not referenced
#endif
TEST_CASE("README", "[hide]")
{
{
......@@ -298,3 +303,7 @@ TEST_CASE("README", "[hide]")
std::cout.rdbuf(old_cout_buffer);
}
}
#if defined(_MSC_VER)
#pragma warning (pop)
#endif
......@@ -1233,4 +1233,24 @@ TEST_CASE("regression tests")
"[json.exception.type_error.302] type must be array, but is null");
}
}
SECTION("issue #367 - Behavior of operator>> should more closely resemble that of built-in overloads.")
{
SECTION("example 1")
{
std::istringstream i1_2_3( "{\"first\": \"one\" }{\"second\": \"two\"}3" );
json j1, j2, j3;
i1_2_3 >> j1;
i1_2_3 >> j2;
i1_2_3 >> j3;
std::map<std::string,std::string> m1 = j1;
std::map<std::string,std::string> m2 = j2;
int i3 = j3;
CHECK( m1 == ( std::map<std::string,std::string> {{ "first", "one" }} ));
CHECK( m2 == ( std::map<std::string,std::string> {{ "second", "two" }} ));
CHECK( i3 == 3 );
}
}
}
......@@ -201,7 +201,7 @@ void from_json(const BasicJsonType& j, country& c)
{
{u8"中华人民共和国", country::china},
{"France", country::france},
{"Российская Федерация", country::russia}
{u8"Российская Федерация", country::russia}
};
const auto it = m.find(str);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment