Compare commits

...

16 Commits

Author SHA1 Message Date
Valentin Balaschenko
4f7000e93c results_3.5.6_3.6.2_3.6.3 2026-07-08 17:27:17 +01:00
Valentin Balaschenko
2133ba64bf 3.5.6_3.6.2 2026-07-08 15:57:53 +01:00
Valentin Balaschenko
9b7582be93 test 1 2026-07-08 15:08:40 +01:00
Valentin Balaschenko
d560622c03 namespace fix 2026-07-08 15:01:20 +01:00
Valentin Balaschenko
027435be85 Merge branch 'develop' into vlntb/pratik-openssl-performance-test
# Conflicts:
#	conan.lock
#	conanfile.py
2026-07-08 12:46:29 +01:00
Ayaz Salikhov
e372c45836 chore: Enable most performance checks (#7727) 2026-07-07 21:03:56 +00:00
Ayaz Salikhov
2ebc96a4a6 perf: Use std::from/to_chars for JSON double parsing/formating (#7735) 2026-07-07 21:02:39 +00:00
Pratik Mankawde
20c8e16cf7 openssl locked to 3.5.6
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-05-26 16:03:38 +01:00
Pratik Mankawde
12c087c8b6 Merge branch 'develop' into pratik/openssl-3.6.0-alpha-performance-test
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-05-26 15:58:05 +01:00
Pratik Mankawde
427f813cea cleaned up
Signed-off-by: Pratik Mankawde <pmankawde@ripple.com>
2025-11-12 15:19:39 +00:00
Pratik Mankawde
7d2ec291de Merge branch 'develop' into pratik/openssl-3.6.0-alpha-performance-test 2025-11-12 14:25:04 +00:00
Pratik Mankawde
3cc645c1fe Merge branch 'develop' into pratik/openssl-3.6.0-alpha-performance-test
Signed-off-by: Pratik Mankawde <pmankawde@ripple.com>
2025-11-12 10:57:22 +00:00
Pratik Mankawde
c8877ec45d added a unit test for openssl hash calculation
Signed-off-by: Pratik Mankawde <pmankawde@ripple.com>
2025-10-22 13:01:04 +01:00
Pratik Mankawde
c4f1313d29 ignore deprecated
Signed-off-by: Pratik Mankawde <pmankawde@ripple.com>
2025-10-02 17:36:39 +01:00
Pratik Mankawde
f770aac75c deleted conan.lock file
Signed-off-by: Pratik Mankawde <pmankawde@ripple.com>
2025-10-02 17:02:20 +01:00
Pratik Mankawde
2cac24512c Tweaked Conan scripts to build against openssl3.6.0
Signed-off-by: Pratik Mankawde <pmankawde@ripple.com>
2025-10-02 16:58:47 +01:00
18 changed files with 330 additions and 78 deletions

View File

@@ -50,13 +50,7 @@ Checks: "-*,
performance-*,
-performance-avoid-endl,
-performance-enum-size,
-performance-inefficient-algorithm,
-performance-inefficient-string-concatenation,
-performance-no-int-to-ptr,
-performance-noexcept-destructor,
-performance-noexcept-move-constructor,
-performance-noexcept-swap,
-performance-type-promotion-in-math-fn,
-performance-unnecessary-copy-initialization,
-performance-unnecessary-value-param,

View File

@@ -23,7 +23,11 @@ include(CompilationEnv)
if(is_gcc)
# GCC-specific fixes
add_compile_options(-Wno-unknown-pragmas -Wno-subobject-linkage)
add_compile_options(
-Wno-unknown-pragmas
-Wno-subobject-linkage
-Wno-error=deprecated-declarations
)
# -Wno-subobject-linkage can be removed when we upgrade GCC version to at least 13.3
elseif(is_clang)
# Clang-specific fixes
@@ -83,7 +87,11 @@ if(only_docs)
endif()
include(deps/Boost)
find_package(OpenSSL 3.5.6 REQUIRED)
set_target_properties(
OpenSSL::SSL
PROPERTIES INTERFACE_COMPILE_DEFINITIONS OPENSSL_NO_SSL2
)
add_subdirectory(external/antithesis-sdk)
find_package(date REQUIRED)
find_package(ed25519 REQUIRED)

View File

@@ -1,44 +1,44 @@
{
"version": "0.5",
"requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1743678659.187",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1778091117.311",
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1751554127.172",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246",
"secp256k1/0.7.1#b1f450b7f78a36fff75bb6934a356f3a%1782338841.3729",
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1782392413.075713",
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1782392402.431897",
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1762797952.535",
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1774398111.888",
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
"openssl/3.6.3#1163d4ddc603907084d08a6a0c6e580f%1782307150.583886",
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1782392402.297166",
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1775040983.408",
"mpt-crypto/0.4.0-rc2#a580f2f9ad0e795de696aa62d54fb9af%1782425834.488828",
"lz4/1.10.0#982d9b673900f665a1da109e09c17cab%1782392402.164188",
"libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1782392792.775744",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1782392402.420732",
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1782392403.066892",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1722218217.276",
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1778091117.848",
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
"grpc/1.81.1#5217e6ef0544c42b46f4af35d5e7f649%1782307148.845616",
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1782392402.296732",
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1782392419.475605",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778091165.282",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
],
"build_requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1782395692.540639",
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1782395690.33162",
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
"m4/1.4.19#34c4bbc3eeebe98ca6edf2f52d602e7d%1777282960.259",
"cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1782392418.696091",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447",
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
],
"python_requires": [],
"overrides": {
@@ -62,4 +62,4 @@
]
},
"config_requires": []
}
}

View File

@@ -4,6 +4,8 @@ from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
from conan import ConanFile
import subprocess
class Xrpl(ConanFile):
name = "xrpl"

View File

@@ -641,6 +641,9 @@ template <class T>
T*
SharedWeakUnion<T>::unsafeGetRawPtr() const
{
// tp_ packs a raw pointer together with a strength bit; recovering the
// pointer inherently requires an integer-to-pointer cast.
// NOLINTNEXTLINE(performance-no-int-to-ptr)
return reinterpret_cast<T*>(tp_ & kPtrMask);
}

View File

@@ -507,6 +507,9 @@ TaggedPointer::operator=(TaggedPointer&& other)
[[nodiscard]] inline std::pair<std::uint8_t, void*>
TaggedPointer::decode() const
{
// tp_ packs a raw pointer together with the tag bits; recovering the
// pointer inherently requires an integer-to-pointer cast.
// NOLINTNEXTLINE(performance-no-int-to-ptr)
return {tp_ & kTagMask, reinterpret_cast<void*>(tp_ & kPtrMask)};
}
@@ -535,6 +538,9 @@ TaggedPointer::getHashesAndChildren() const
[[nodiscard]] inline SHAMapHash*
TaggedPointer::getHashes() const
{
// tp_ packs a raw pointer together with the tag bits; recovering the
// pointer inherently requires an integer-to-pointer cast.
// NOLINTNEXTLINE(performance-no-int-to-ptr)
return reinterpret_cast<SHAMapHash*>(tp_ & kPtrMask);
};

37
run_openssl_comparison.sh Executable file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Compare SHA-512-half performance across OpenSSL versions.
# Binaries are identical builds (GCC 13.3, Release, same locked deps),
# differing only in the statically linked OpenSSL.
set -euo pipefail
cd "$(dirname "$0")/.build"
V356=./xrpl_tests-openssl-3.5.6
V362=./xrpl_tests-openssl-3.6.2
V363=./xrpl_tests-openssl-3.6.3
# Single hash of the full 100 KB buffer. Startup-dominated (~ms), kept for
# parity with earlier result logs; expect near-identical numbers.
FILTER=OpenSSL.SingleHashFullSlice
hyperfine -N \
--warmup 5 \
--min-runs 200 \
--command-name "openssl-3.5.6 singlehash" "$V356 --gtest_filter=$FILTER" \
--command-name "openssl-3.6.2 singlehash" "$V362 --gtest_filter=$FILTER" \
--command-name "openssl-3.6.3 singlehash" "$V363 --gtest_filter=$FILTER" \
--export-markdown openssl_comparison_singlehash.md \
--export-json openssl_comparison_singlehash.json
# 100k hashes per run; the meaningful benchmark.
FILTER=OpenSSL.MultihashAllSlices
hyperfine \
--warmup 5 \
--min-runs 20 \
--command-name "openssl-3.5.6 multihash" "$V356 --gtest_filter=$FILTER" \
--command-name "openssl-3.6.2 multihash" "$V362 --gtest_filter=$FILTER" \
--command-name "openssl-3.6.3 multihash" "$V363 --gtest_filter=$FILTER" \
--export-markdown openssl_comparison.md \
--export-json openssl_comparison.json
echo
echo "Results saved to .build/openssl_comparison*.md and .build/openssl_comparison*.json"

2
run_singlehash.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/usr/bin/env bash
./.build/xrpl_tests --gtest_filter=OpenSSL.SingleHashFullSlice

View File

@@ -5,12 +5,13 @@
#include <algorithm>
#include <cctype>
#include <charconv>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <istream>
#include <stdexcept>
#include <string>
#include <system_error>
namespace json {
// Implementation of class Reader
@@ -605,36 +606,17 @@ bool
Reader::decodeDouble(Token& token)
{
double value = 0;
int const bufferSize = 32;
int count = 0;
int const length = int(token.end - token.start);
// Sanity check to avoid buffer overflow exploits.
if (length < 0)
{
return addError("Unable to parse token length", token);
}
// Avoid using a string constant for the format control string given to
// sscanf, as this can cause hard to debug crashes on OS X. See here for
// more info:
//
// http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
char format[] = "%lf";
if (length <= bufferSize)
{
Char buffer[bufferSize + 1];
memcpy(buffer, token.start, length);
buffer[length] = 0;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
count = sscanf(buffer, format, &value);
}
else
{
std::string const buffer(token.start, token.end);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
count = sscanf(buffer.c_str(), format, &value);
}
if (count != 1)
auto const [ptr, ec] = std::from_chars(token.start, token.end, value);
// Reject anything from_chars could not turn into a finite double:
// - ec != std::errc{}: no valid conversion, or an out-of-range magnitude
// (e.g. 1e400).
// - ptr != token.end: readNumber() is permissive about which characters
// it collects into a token (it will, for example, keep a '+' mid-token),
// but from_chars() will stop at the first character it cannot parse.
if (ec != std::errc{} || ptr != token.end)
return addError("'" + std::string(token.start, token.end) + "' is not a number.", token);
currentValue() = value;
return true;
}

View File

@@ -4,13 +4,14 @@
#include <xrpl/json/json_forwards.h>
#include <xrpl/json/json_value.h>
#include <cstdio>
#include <charconv>
#include <cstring>
#include <iomanip>
#include <ios>
#include <ostream>
#include <sstream>
#include <string>
#include <system_error>
#include <utility>
namespace json {
@@ -76,20 +77,18 @@ valueToString(UInt value)
std::string
valueToString(double value)
{
// Allocate a buffer that is more than large enough to store the 16 digits
// of precision requested below.
// Format with 16 significant digits.
// We need not request the alternative representation that always has a
// decimal point because JSON doesn't distinguish the concepts of reals and integers.
// A double never needs more than 32 characters in this form,
// so to_chars cannot actually run out of room here.
char buffer[32];
// Print into the buffer. We need not request the alternative representation
// that always has a decimal point because JSON doesn't distinguish the
// concepts of reals and integers.
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005
// to avoid warning.
sprintf_s(buffer, sizeof(buffer), "%.16g", value);
#else
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
snprintf(buffer, sizeof(buffer), "%.16g", value);
#endif
return buffer;
auto const [ptr, ec] =
std::to_chars(buffer, buffer + sizeof(buffer), value, std::chars_format::general, 16);
XRPL_ASSERT(ec == std::errc{}, "json::valueToString(double) : conversion fits buffer");
if (ec != std::errc{})
return {};
return std::string(buffer, ptr);
}
std::string

View File

@@ -69,6 +69,17 @@ toUnsigned(U2 value)
return static_cast<U1>(value);
}
static std::string
joinName(std::string const& jsonName, std::string const& fieldName)
{
std::string result;
result.reserve(jsonName.size() + 1 + fieldName.size());
result += jsonName;
result += '.';
result += fieldName;
return result;
}
// LCOV_EXCL_START
static inline std::string
makeName(std::string const& object, std::string const& field)
@@ -76,7 +87,7 @@ makeName(std::string const& object, std::string const& field)
if (field.empty())
return object;
return object + "." + field;
return joinName(object, field);
}
static inline json::Value
@@ -1036,8 +1047,8 @@ parseObject(
try
{
auto ret =
parseObject(jsonName + "." + fieldName, value, field, depth + 1, error);
auto ret = parseObject(
joinName(jsonName, fieldName), value, field, depth + 1, error);
if (!ret)
return std::nullopt;
data.emplaceBack(std::move(*ret));
@@ -1054,8 +1065,8 @@ parseObject(
case STI_ARRAY:
try
{
auto array =
parseArray(jsonName + "." + fieldName, value, field, depth + 1, error);
auto array = parseArray(
joinName(jsonName, fieldName), value, field, depth + 1, error);
if (!array.has_value())
return std::nullopt;
data.emplaceBack(std::move(*array));

View File

@@ -213,6 +213,12 @@ public:
if (auto [conn, keepAlive] = getConnection(); conn)
{
(void)keepAlive;
// The checkpointer is identified to the C callback by an integer id
// (resolved via checkpointerFromId) rather than a raw `this`, so it
// cannot dangle if the checkpointer is destroyed. Passing the id
// through sqlite's void* user-data requires an integer-to-pointer
// cast.
// NOLINTNEXTLINE(performance-no-int-to-ptr)
sqlite_api::sqlite3_wal_hook(conn, &sqliteWALHook, reinterpret_cast<void*>(id_));
}
}

View File

@@ -1475,6 +1475,7 @@ r.ripple.com:51235
std::string toLoad(R"xrpldConfig(
[amendment_majority_time]
)xrpldConfig");
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
toLoad += std::to_string(val) + space + unit;
space = space.empty() ? " " : "";

View File

@@ -0,0 +1,40 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/protocol/digest.h>
#include <gtest/gtest.h>
#include <cmath>
#include <cstddef>
#include <random>
#include <vector>
namespace xrpl::test {
static std::vector<char> const data = []() {
std::vector<char> strV(pow(10, 5));
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dis(32, 127);
for (size_t index = 0; index < strV.size(); ++index)
{
strV[index] = static_cast<char>(dis(gen));
}
return strV;
}();
TEST(OpenSSL, SingleHashFullSlice)
{
Slice const s{data.data(), data.size()};
[[maybe_unused]] auto hash = sha512Half(s);
}
TEST(OpenSSL, MultihashAllSlices)
{
for (std::size_t i = 0; i < data.size(); ++i)
{
Slice s(&data[i], data.size() - i);
[[maybe_unused]] auto hash = sha512Half(s);
}
}
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
-------------------------------------------------------------------------------------------------
Openssl 3.6.0: https://github.com/XRPLF/rippled/tree/pratik/openssl-3.6.0-alpha-performance-test
valentin@valentin-Thelio-Major:~/projects/rippled3/rippled$ hyperfine -N --warmup 5 --min-runs 200 -- ./run_singlehash.sh
Benchmark 1: ./run_singlehash.sh
Time (mean ± σ): 4.5 ms ± 0.4 ms [User: 2.3 ms, System: 2.1 ms]
Range (min … max): 3.7 ms … 6.2 ms 673 runs
valentin@valentin-Thelio-Major:~/projects/rippled3/rippled$ hyperfine --warmup 5 --min-runs 20 './.build/src/tests/libxrpl/xrpl.test.basics --test-case="*MultihashAllSlices*" -s'
Benchmark 1: ./.build/src/tests/libxrpl/xrpl.test.basics --test-case="*MultihashAllSlices*" -s
Time (mean ± σ): 5.636 s ± 0.005 s [User: 5.633 s, System: 0.002 s]
Range (min … max): 5.630 s … 5.651 s 20 runs
----------------------------------------------------------------------------------------------
Openssl 3.5.4: https://github.com/XRPLF/rippled/tree/pratik/openssl_354_test
hyperfine -N --warmup 5 --min-runs 200 -- ./run_singlehash.sh
Benchmark 1: ./run_singlehash.sh
Time (mean ± σ): 4.5 ms ± 0.4 ms [User: 2.3 ms, System: 2.1 ms]
Range (min … max): 3.7 ms … 6.0 ms 663 runs
valentin@valentin-Thelio-Major:~/projects/rippled3/rippled$ hyperfine --warmup 5 --min-runs 20 './.build/src/tests/libxrpl/xrpl.test.basics --test-case="*MultihashAllSlices*" -s'
Benchmark 1: ./.build/src/tests/libxrpl/xrpl.test.basics --test-case="*MultihashAllSlices*" -s
Time (mean ± σ): 3.968 s ± 0.003 s [User: 3.966 s, System: 0.002 s]
Range (min … max): 3.965 s … 3.977 s 20 runs
----------------------------------------------------------------------------------------------
Openssl 1.1.1: https://github.com/XRPLF/rippled/tree/pratik/openssl_111_perf_test
valentin@valentin-Thelio-Major:~/projects/rippled3/rippled$ hyperfine -N --warmup 5 --min-runs 200 -- ./run_singlehash.sh
Benchmark 1: ./run_singlehash.sh
Time (mean ± σ): 4.4 ms ± 0.4 ms [User: 2.3 ms, System: 2.0 ms]
Range (min … max): 3.7 ms … 6.1 ms 623 runs
valentin@valentin-Thelio-Major:~/projects/rippled3/rippled$ hyperfine --warmup 5 --min-runs 20 './.build/src/tests/libxrpl/xrpl.test.basics --test-case="*MultihashAllSlices*" -s'
Benchmark 1: ./.build/src/tests/libxrpl/xrpl.test.basics --test-case="*MultihashAllSlices*" -s
Time (mean ± σ): 3.969 s ± 0.004 s [User: 3.966 s, System: 0.002 s]
Range (min … max): 3.964 s … 3.980 s 20 runs

View File

@@ -0,0 +1,35 @@
-------------------------------------------------------------------------------------------------
OpenSSL 3.5.6 vs 3.6.2 comparison
Date: 2026-07-08
Branch: vlntb/pratik-openssl-performance-test (commit 9b7582be93)
Machine: valentin-Thelio-Major (Ubuntu 24.04, 48 cores)
Both binaries are identical Release builds (GCC 13.3, C++23, same conan.lock),
differing only in the statically linked OpenSSL version (verified via the
embedded version banner: grep -aom1 'OpenSSL 3\.' <binary>).
Tests were ported from doctest to GTest, so the runner differs from the
openssl_test_results_1 runs: single combined binary xrpl_tests, --gtest_filter
instead of --test-case. Old absolute numbers are not directly comparable
(different compiler: GCC 12 vs GCC 13).
Benchmark script: run_openssl_comparison.sh (hyperfine 1.18.0)
SingleHashFullSlice (hyperfine -N --warmup 5 --min-runs 200)
Note: startup-dominated (~ms); one 100 KB hash takes microseconds.
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---------------------------|----------:|---------:|---------:|------------:|
| openssl-3.5.6 singlehash | 4.6 ± 0.5 | 3.9 | 7.4 | 1.02 ± 0.14 |
| openssl-3.6.2 singlehash | 4.5 ± 0.5 | 3.9 | 6.5 | 1.00 |
MultihashAllSlices (hyperfine --warmup 5 --min-runs 20; 100k sha512Half calls per run)
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---------------------------|--------------:|--------:|--------:|------------:|
| openssl-3.5.6 multihash | 4.084 ± 0.021 | 4.056 | 4.123 | 1.01 ± 0.01 |
| openssl-3.6.2 multihash | 4.060 ± 0.011 | 4.046 | 4.086 | 1.00 |
Conclusion: 3.6.2 is on par with 3.5.6 on both benchmarks (differences within
noise). The ~42% SHA-512 regression observed with the 3.6.0 alpha (see
openssl_test_results_1: 5.636 s vs 3.968 s for 3.5.4) is fixed in the 3.6.2
release. No performance objection to upgrading.

View File

@@ -0,0 +1,35 @@
-------------------------------------------------------------------------------------------------
OpenSSL 3.5.6 vs 3.6.2 vs 3.6.3 comparison
Date: 2026-07-08
Branch: vlntb/pratik-openssl-performance-test (commit 2133ba64bf)
Machine: valentin-Thelio-Major (Ubuntu 24.04, 48 cores)
All three binaries are identical Release builds (GCC 13.3, C++23, same
conan.lock apart from the openssl entry), differing only in the statically
linked OpenSSL version (verified via the embedded version banner:
grep -aom1 'OpenSSL 3\.' <binary>). 3.5.6/3.6.2 recipes from conancenter,
3.6.3 from the xrplf remote.
Benchmark script: run_openssl_comparison.sh (hyperfine 1.18.0)
SingleHashFullSlice (hyperfine -N --warmup 5 --min-runs 200)
Note: startup-dominated (~ms); one 100 KB hash takes microseconds.
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---------------------------|----------:|---------:|---------:|------------:|
| openssl-3.5.6 singlehash | 4.6 ± 0.5 | 3.9 | 8.2 | 1.03 ± 0.15 |
| openssl-3.6.2 singlehash | 4.6 ± 0.4 | 3.9 | 6.5 | 1.01 ± 0.14 |
| openssl-3.6.3 singlehash | 4.5 ± 0.4 | 3.9 | 6.2 | 1.00 |
MultihashAllSlices (hyperfine --warmup 5 --min-runs 20; 100k sha512Half calls per run)
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---------------------------|--------------:|--------:|--------:|------------:|
| openssl-3.5.6 multihash | 4.083 ± 0.015 | 4.060 | 4.117 | 1.00 ± 0.01 |
| openssl-3.6.2 multihash | 4.074 ± 0.017 | 4.049 | 4.116 | 1.00 |
| openssl-3.6.3 multihash | 4.081 ± 0.028 | 4.047 | 4.125 | 1.00 ± 0.01 |
Conclusion: all three versions are statistically indistinguishable on both
benchmarks. The ~42% SHA-512 regression observed with the 3.6.0 alpha (see
openssl_test_results_1) remains fixed in 3.6.3. No performance objection to
upgrading to either 3.6.2 or 3.6.3.

View File

@@ -13,6 +13,7 @@
#include <exception>
#include <limits>
#include <numbers>
#include <optional>
#include <regex>
#include <sstream>
#include <string>
@@ -592,6 +593,57 @@ TEST(json_value, bad_json)
EXPECT_TRUE(r.parse(s, j));
}
namespace {
std::optional<json::Value>
parseValue(std::string const& doc)
{
json::Value j;
json::Reader r;
if (!r.parse("{\"v\":" + doc + "}", j))
return std::nullopt;
return j["v"];
}
} // namespace
TEST(json_value, parse_double_valid)
{
// 1e300 is large but still representable, so it parses (unlike the out-of-range cases below).
for (auto const& [text, expected] :
{std::pair{"2.5", 2.5},
std::pair{"-3.25e2", -325.0},
std::pair{"0.0", 0.0},
std::pair{"1E3", 1000.0},
std::pair{"-0.5e-1", -0.05},
std::pair{"1e300", 1e300}})
{
auto const v = parseValue(text);
ASSERT_TRUE(v.has_value()) << text;
// NOLINTBEGIN(bugprone-unchecked-optional-access)
EXPECT_TRUE(v->isDouble()) << text;
EXPECT_EQ(v->asDouble(), expected) << text;
// NOLINTEND(bugprone-unchecked-optional-access)
}
}
TEST(json_value, parse_double_out_of_range)
{
// Magnitudes with no finite double representation are rejected.
for (char const* oor : {"1e400", "-1e400", "0.001e500", "1e-400", "-1e-400", "123e-500"})
EXPECT_FALSE(parseValue(oor).has_value()) << oor;
}
TEST(json_value, parse_double_malformed)
{
// readNumber() collects any run of digits and '.eE+-' into a single Double
// token, so these malformed tokens reach decodeDouble. Each has a valid
// leading prefix that from_chars would accept on its own; requiring the
// entire token be consumed rejects them instead of silently truncating.
for (char const* bad : {"1+2", "1-2", "1.2.3", "1e5e6", "1..2", "++5", "1e", "1e+", ".", "-"})
EXPECT_FALSE(parseValue(bad).has_value()) << bad;
}
TEST(json_value, edge_cases)
{
std::uint32_t const maxUInt = std::numeric_limits<std::uint32_t>::max();