From 10a618933142d62957603c9db17a6f8e6c83c827 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Tue, 18 May 2021 15:02:15 -0700 Subject: [PATCH 1/3] Thread safe Mersenne Twister 64 using c++11 --- include/tscore/Random.h | 56 ++++++++++++++++++++++++++++ src/tscore/Makefile.am | 4 +- src/tscore/Random.cc | 31 +++++++++++++++ src/tscore/unit_tests/test_Random.cc | 52 ++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 include/tscore/Random.h create mode 100644 src/tscore/Random.cc create mode 100644 src/tscore/unit_tests/test_Random.cc diff --git a/include/tscore/Random.h b/include/tscore/Random.h new file mode 100644 index 00000000000..643bd8a7c6b --- /dev/null +++ b/include/tscore/Random.h @@ -0,0 +1,56 @@ +/** @file + + Pseudorandom Number Generator + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include + +namespace ts +{ +class Random +{ +public: + Random() = delete; + + static uint64_t + random() + { + return _dist_int(_engine); + } + + static double + drandom() + { + return _dist_real(_engine); + } + + static void + seed(uint64_t s) + { + _engine.seed(s); + } + +private: + thread_local static std::mt19937_64 _engine; + thread_local static std::uniform_int_distribution _dist_int; + thread_local static std::uniform_real_distribution _dist_real; +}; +}; // namespace ts diff --git a/src/tscore/Makefile.am b/src/tscore/Makefile.am index 2f73d7b7fad..9610b2216b9 100644 --- a/src/tscore/Makefile.am +++ b/src/tscore/Makefile.am @@ -110,6 +110,7 @@ libtscore_la_SOURCES = \ MemArena.cc \ MMH.cc \ ParseRules.cc \ + Random.cc \ RbTree.cc \ Regex.cc \ Regression.cc \ @@ -190,7 +191,8 @@ test_tscore_SOURCES = \ unit_tests/test_ts_file.cc \ unit_tests/test_Version.cc \ unit_tests/test_Errata.cc \ - unit_tests/test_MMH.cc + unit_tests/test_MMH.cc \ + unit_tests/test_Random.cc if HAS_HKDF test_tscore_SOURCES += \ diff --git a/src/tscore/Random.cc b/src/tscore/Random.cc new file mode 100644 index 00000000000..c6accc3d0a5 --- /dev/null +++ b/src/tscore/Random.cc @@ -0,0 +1,31 @@ +/** @file + + Pseudorandom Number Generator + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "tscore/Random.h" + +namespace ts +{ +thread_local std::mt19937_64 Random::_engine{std::random_device{}()}; +thread_local std::uniform_int_distribution Random::_dist_int{0, UINT64_MAX}; +thread_local std::uniform_real_distribution Random::_dist_real{0, 1}; +}; // namespace ts diff --git a/src/tscore/unit_tests/test_Random.cc b/src/tscore/unit_tests/test_Random.cc new file mode 100644 index 00000000000..6b8b8e620d9 --- /dev/null +++ b/src/tscore/unit_tests/test_Random.cc @@ -0,0 +1,52 @@ +/** @file + + Pseudorandom Number Generator test + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "catch.hpp" +#include "tscore/Random.h" +#include "tscore/ink_rand.h" +#include + +TEST_CASE("test random", "[libts][random]") +{ + ts::Random::seed(13); + InkRand x(13); + + for (auto i = 0; i < 1000000; ++i) { + REQUIRE(ts::Random::random() == x.random()); + REQUIRE(uint64_t(ts::Random::drandom() * 100000000) == uint64_t(x.drandom() * 100000000)); // they are pretty close to the same + } + + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < 1000000; ++i) { + ts::Random::random(); + } + auto diff = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << (double)diff.count() / 1000000 << " ns per Random::random()" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < 1000000; ++i) { + x.random(); + } + diff = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << (double)diff.count() / 1000000 << " ns per Random::random()" << std::endl; +} From 2750fe706cd1c4db0c55200787bdfa155d9ded16 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Wed, 19 May 2021 10:36:39 -0700 Subject: [PATCH 2/3] Updated output on test --- src/tscore/unit_tests/test_Random.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tscore/unit_tests/test_Random.cc b/src/tscore/unit_tests/test_Random.cc index 6b8b8e620d9..ab8ecf0581f 100644 --- a/src/tscore/unit_tests/test_Random.cc +++ b/src/tscore/unit_tests/test_Random.cc @@ -48,5 +48,5 @@ TEST_CASE("test random", "[libts][random]") x.random(); } diff = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); - std::cout << (double)diff.count() / 1000000 << " ns per Random::random()" << std::endl; + std::cout << (double)diff.count() / 1000000 << " ns per InkRand::random()" << std::endl; } From bd88586437a4b24fc2cd34aed05171a2586b1107 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Wed, 19 May 2021 11:51:22 -0700 Subject: [PATCH 3/3] Reduce the number of threa_local vars and update test output --- include/tscore/Random.h | 8 ++++---- src/tscore/Random.cc | 2 -- src/tscore/unit_tests/test_Random.cc | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/include/tscore/Random.h b/include/tscore/Random.h index 643bd8a7c6b..6e13001b6a8 100644 --- a/include/tscore/Random.h +++ b/include/tscore/Random.h @@ -33,13 +33,15 @@ class Random static uint64_t random() { - return _dist_int(_engine); + std::uniform_int_distribution dist{0, UINT64_MAX}; + return dist(_engine); } static double drandom() { - return _dist_real(_engine); + std::uniform_real_distribution dist{0, 1}; + return dist(_engine); } static void @@ -50,7 +52,5 @@ class Random private: thread_local static std::mt19937_64 _engine; - thread_local static std::uniform_int_distribution _dist_int; - thread_local static std::uniform_real_distribution _dist_real; }; }; // namespace ts diff --git a/src/tscore/Random.cc b/src/tscore/Random.cc index c6accc3d0a5..155373f6d97 100644 --- a/src/tscore/Random.cc +++ b/src/tscore/Random.cc @@ -26,6 +26,4 @@ namespace ts { thread_local std::mt19937_64 Random::_engine{std::random_device{}()}; -thread_local std::uniform_int_distribution Random::_dist_int{0, UINT64_MAX}; -thread_local std::uniform_real_distribution Random::_dist_real{0, 1}; }; // namespace ts diff --git a/src/tscore/unit_tests/test_Random.cc b/src/tscore/unit_tests/test_Random.cc index ab8ecf0581f..7d4b6a6619e 100644 --- a/src/tscore/unit_tests/test_Random.cc +++ b/src/tscore/unit_tests/test_Random.cc @@ -41,7 +41,7 @@ TEST_CASE("test random", "[libts][random]") ts::Random::random(); } auto diff = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); - std::cout << (double)diff.count() / 1000000 << " ns per Random::random()" << std::endl; + std::cout << std::endl << (double)diff.count() / 1000000 << " ns per Random::random()" << std::endl; start = std::chrono::high_resolution_clock::now(); for (auto i = 0; i < 1000000; ++i) {