Randoms, String refactor, Time, System, + Profiler improvements

v1
Brett 2023-01-05 01:52:56 -05:00
parent f1254d54c3
commit beff47b8f0
11 changed files with 250 additions and 206 deletions

View File

@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.24)
project(BLT)
set(CMAKE_PROJECT_VERSION 0.1a)
set(CMAKE_CXX_STANDARD 17)
option(BUILD_STD "Build the BLT standard utilities." ON)
@ -18,14 +19,11 @@ else()
set(PROFILING_FILES "")
endif()
#include parallel hashmaps if the user decided to download the submodule.
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap/CMakeLists.txt)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap/)
set(PHMAP_ENABLED ON)
endif()
configure_file(include/blt/config.h.in blt/config.h @ONLY)
# include the config file
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(include/)
message("Standard Files ${STD_FILES}")
@ -35,5 +33,6 @@ message("Current Source: ${CMAKE_CURRENT_SOURCE_DIR}")
add_library(BLT ${STD_FILES} ${PROFILING_FILES})
target_include_directories(BLT PUBLIC include/)
target_include_directories(BLT PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(BLT phmap)
message("BLT ${CMAKE_PROJECT_VERSION} Successfully included!")

View File

@ -1,2 +1,3 @@
Utility classes start with captials (IE classes which are purely static)
everything else is camel cased underscore to conform to what most of the C libs seem to use
Template types should be capital camel case

View File

@ -1,12 +0,0 @@
/*
* Created by Brett on 27/12/22.
* Licensed under GNU General Public License V3.0
* See LICENSE file for license detail
*/
#ifndef CMAKE_CONFIG
#cmakedefine PHMAP_ENABLED
#define CMAKE_CONFIG
#endif

View File

@ -9,58 +9,68 @@
#include <string>
#include <string_view>
#include <blt/config.h>
#include <mutex>
#include <queue>
#ifdef PHMAP_ENABLED
#include <parallel_hashmap/phmap.h>
#else
#include <unordered_map>
#endif
#include <blt/std/time.h>
namespace BLT {
struct CapturePoint {
std::string_view name;
long point;
};
struct CaptureInterval {
CapturePoint start;
CapturePoint end;
long start;
long end;
};
#ifdef PHMAP_ENABLED
typedef phmap::parallel_flat_hash_map<std::string_view, CaptureInterval> INTERVAL_MAP;
typedef phmap::parallel_flat_hash_map<std::string_view, std::queue<CapturePoint>> POINT_MAP;
typedef phmap::parallel_flat_hash_map<std::string_view, POINT_MAP> POINT_HISTORY_MAP;
typedef phmap::parallel_flat_hash_map<int, std::string_view> ORDER_MAP;
#else
typedef std::unordered_map<std::string_view, CaptureInterval> INTERVAL_MAP;
typedef std::unordered_map<std::string_view, CapturePoint> POINT_MAP;
typedef std::unordered_map<std::string_view, CapturePoint> POINT_HISTORY_MAP;
typedef std::unordered_map<int, std::string_view> ORDER_MAP;
#endif
class Profiler {
/**
* @tparam HASHMAP_TYPE
*/
template <template<typename, typename> class HASHMAP_TYPE>
class profile {
private:
INTERVAL_MAP intervals{};
// profiling intervals.
HASHMAP_TYPE<std::string_view, CaptureInterval> intervals{};
// profiling points
std::vector<std::queue<CapturePoint>> cyclicPointsHistory{};
std::queue<CapturePoint> points{};
std::queue<CapturePoint> cyclicPoints{};
POINT_HISTORY_MAP cyclicPointsHistory{};
ORDER_MAP order{};
std::mutex timerLock{};
int lastOrder = 0;
public:
Profiler() = default;
void finishCycle();
void startInterval(const std::string_view& name);
void endInterval(const std::string_view& name);
void profilerPoint();
profile() = default;
void finishCycle() {
cyclicPointsHistory.push_back(cyclicPoints);
// im not sure if this is the correct way to clear a queue, there is no function to do so.
cyclicPoints = {};
}
void startInterval(const std::string_view& name) {
std::scoped_lock lock(timerLock);
CaptureInterval interval{};
interval.start = System::getCurrentTimeNanoseconds();
intervals[name] = interval;
}
void endInterval(const std::string_view& name) {
std::scoped_lock lock(timerLock);
intervals[name].end = System::getCurrentTimeNanoseconds();
}
/**
* Uses a separate tracking device that will be reset when finishCycle(); is called.
* Records the current time for the purpose of reconstructing the execution time between points, in order to find the most common cause for performance issues.
* @param name a common name for the point which you are trying to profile. This name should be meaningful as it will be displayed in the output.
*/
void profilerPointCyclic();
void profilerPoint(const std::string_view& name) {
points.push(CapturePoint{name, System::getCurrentTimeNanoseconds()});
}
/**
* Records the current time for the purpose of reconstructing the execution time between points, in order to find the most common cause for performance issues.
* Uses a separate tracking device that will be reset when finishCycle(); is called.
* @param name a common name for the point which you are trying to profile. This name should be meaningful as it will be displayed in the output.
*/
void profilerPointCyclic(const std::string_view& name) {
cyclicPoints.push(CapturePoint{name, System::getCurrentTimeNanoseconds()});
}
};
}

View File

@ -55,17 +55,24 @@ namespace BLT {
data[insertIndex++] = t;
}
/**
* Warning does not contain runtime error checking!
* @return the element at the "front" of the queue.
*/
[[nodiscard]] const T& front() const {
return data[headIndex];
}
void pop() {
// TODO: throw exception when popping would result in a overflow?
// I didn't make it an exception here due to not wanting to import the class.
if (headIndex >= size)
return;
headIndex++;
// queue is empty. Clear old data.
if (headIndex >= size){
delete[] data;
data = new T[size];
}
}
bool isEmpty(){
return headIndex >= size;
}
~flat_queue() {
@ -73,6 +80,7 @@ namespace BLT {
}
};
// avoid this. it is very slow.
template<typename T>
class node_queue {
private:

View File

@ -16,19 +16,29 @@ namespace BLT {
* @tparam dist std::uniform_real_distribution or std::uniform_int_distribution
*/
template<typename T, template<typename = T> typename dist = std::uniform_real_distribution>
class Random {
class random {
private:
std::random_device rd; // obtain a random number from hardware
std::mt19937 gen;
dist<T>* distribution = nullptr;
public:
explicit Random(T min = (T)0, T max = (T)1, long seed = 0): gen(std::mt19937(seed)){
/**
* Construct the random number generator.
* @param min min value possible to generate. (default: 0)
* @param max max value possible to generate. (default: 1)
* @param seed seed to use in generating random values. (default: 0)
*/
explicit random(T min = (T)0, T max = (T)1, long seed = 0): gen(std::mt19937(seed)){
distribution = new dist(min, max);
}
/**
* Note the min/max are inclusive and defaults to a **uniform** distribution.
* @return random number between the defined min/max or the default of [0,1].
*/
T get(){
return (*distribution)(gen);
}
~Random(){
~random(){
delete distribution;
}
};

View File

@ -11,125 +11,100 @@
#include <sstream>
#include <algorithm>
#include <vector>
#include <ctime>
namespace BLT {
class String {
public:
/**
* Converts the string into lower case
* @param s string to lower case
* @return a string copy that is all lower case
*/
static inline std::string toLowerCase(const std::string& s) {
std::stringstream str;
std::for_each(
s.begin(), s.end(), [&str](unsigned char ch) {
str << (char) std::tolower(ch);
}
);
return str.str();
}
/**
* Converts the string into upper case
* @param s string to upper case
* @return a string copy that is all upper case
*/
static inline std::string toUpperCase(const std::string& s) {
std::stringstream str;
std::for_each(
s.begin(), s.end(), [&str](unsigned char ch) {
str << (char) std::toupper(ch);
}
);
return str.str();
}
/**
* @return the standard string of time.now
*/
static inline std::string getTimeString() {
auto t = std::time(nullptr);
auto now = std::localtime(&t);
std::stringstream timeString;
timeString << (1900 + now->tm_year);
timeString << "-";
timeString << (1 + now->tm_mon);
timeString << "-";
timeString << now->tm_mday;
timeString << " ";
timeString << now->tm_hour;
timeString << ":";
timeString << now->tm_min;
timeString << ":";
timeString << now->tm_sec;
return timeString.str();
}
// taken from https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
// extended to return a vector
static inline std::vector<std::string> split(std::string s, const std::string& delim) {
size_t pos = 0;
std::vector<std::string> tokens;
while ((pos = s.find(delim)) != std::string::npos) {
auto token = s.substr(0, pos);
tokens.push_back(token);
s.erase(0, pos + delim.length());
namespace BLT::String {
/**
* Converts the string into lower case
* @param s string to lower case
* @return a string copy that is all lower case
*/
static inline std::string toLowerCase(const std::string& s) {
std::stringstream str;
std::for_each(
s.begin(), s.end(), [&str](unsigned char ch) {
str << (char) std::tolower(ch);
}
tokens.push_back(s);
return tokens;
}
// taken from https://stackoverflow.com/questions/216823/how-to-trim-an-stdstring
// would've preferred to use boost lib but instructions said to avoid external libs
// trim from start (in place)
static inline std::string& ltrim(std::string& s) {
s.erase(
s.begin(), std::find_if(
s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}
));
return s;
}
// trim from end (in place)
static inline std::string& rtrim(std::string& s) {
s.erase(
std::find_if(
s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch);
}
).base(), s.end());
return s;
}
// trim from both ends (in place)
static inline std::string& trim(std::string& s) {
ltrim(s);
rtrim(s);
return s;
}
// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
ltrim(s);
return s;
}
// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
rtrim(s);
return s;
}
// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
trim(s);
return s;
}
};
);
return str.str();
}
/**
* Converts the string into upper case
* @param s string to upper case
* @return a string copy that is all upper case
*/
static inline std::string toUpperCase(const std::string& s) {
std::stringstream str;
std::for_each(
s.begin(), s.end(), [&str](unsigned char ch) {
str << (char) std::toupper(ch);
}
);
return str.str();
}
// taken from https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
// extended to return a vector
static inline std::vector<std::string> split(std::string s, const std::string& delim) {
size_t pos = 0;
std::vector<std::string> tokens;
while ((pos = s.find(delim)) != std::string::npos) {
auto token = s.substr(0, pos);
tokens.push_back(token);
s.erase(0, pos + delim.length());
}
tokens.push_back(s);
return tokens;
}
// taken from https://stackoverflow.com/questions/216823/how-to-trim-an-stdstring
// would've preferred to use boost lib but instructions said to avoid external libs
// trim from start (in place)
static inline std::string& ltrim(std::string& s) {
s.erase(
s.begin(), std::find_if(
s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}
));
return s;
}
// trim from end (in place)
static inline std::string& rtrim(std::string& s) {
s.erase(
std::find_if(
s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch);
}
).base(), s.end());
return s;
}
// trim from both ends (in place)
static inline std::string& trim(std::string& s) {
ltrim(s);
rtrim(s);
return s;
}
// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
ltrim(s);
return s;
}
// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
rtrim(s);
return s;
}
// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
trim(s);
return s;
}
}
#endif //BLT_STRING_H

14
include/blt/std/system.h Normal file
View File

@ -0,0 +1,14 @@
/*
* Created by Brett on 04/01/23.
* Licensed under GNU General Public License V3.0
* See LICENSE file for license detail
*/
#ifndef BLT_SYSTEM_H
#define BLT_SYSTEM_H
namespace BLT::System {
// TODO: system memory and current CPU usage. (Linux Only currently)
}
#endif //BLT_SYSTEM_H

64
include/blt/std/time.h Normal file
View File

@ -0,0 +1,64 @@
/*
* Created by Brett on 04/01/23.
* Licensed under GNU General Public License V3.0
* See LICENSE file for license detail
*/
#ifndef BLT_TIME_H
#define BLT_TIME_H
#include <chrono>
#include <ctime>
#include <sstream>
namespace BLT::System {
static inline auto getCurrentTimeNanoseconds() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}
/**
* Standard time string is formatted as follows:
* Year-Month-Date Hour:Min:Second
* If you do not want a space in the string use getTimeStringFS(); (Time String for easy filesystem)
* @return the BLT standard string of time.now
*/
static inline std::string getTimeString() {
auto t = std::time(nullptr);
auto now = std::localtime(&t);
std::stringstream timeString;
timeString << (1900 + now->tm_year);
timeString << "-";
timeString << (1 + now->tm_mon);
timeString << "-";
timeString << now->tm_mday;
timeString << " ";
timeString << now->tm_hour;
timeString << ":";
timeString << now->tm_min;
timeString << ":";
timeString << now->tm_sec;
return timeString.str();
}
/**
* @return the BLT standard string of time.now (See getTimeString()) that is filesystem friendly (FAT compatible).
*/
static inline std::string getTimeStringFS() {
auto t = std::time(nullptr);
auto now = std::localtime(&t);
std::stringstream timeString;
timeString << (1900 + now->tm_year);
timeString << "-";
timeString << (1 + now->tm_mon);
timeString << "-";
timeString << now->tm_mday;
timeString << "_";
timeString << now->tm_hour;
timeString << "-";
timeString << now->tm_min;
timeString << "-";
timeString << now->tm_sec;
return timeString.str();
}
}
#endif //BLT_TIME_H

View File

@ -4,38 +4,7 @@
* See LICENSE file for license detail
*/
#include <blt/profiling/profiler.h>
#include <iostream>
#include <utility>
namespace BLT {
void Profiler::finishCycle() {
}
static inline auto getCurrentTimeNanoseconds() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}
void Profiler::startInterval(const std::string_view& name) {
std::scoped_lock lock(timerLock);
CaptureInterval interval{};
interval.start = {getCurrentTimeNanoseconds()};
intervals[name] = interval;
}
void Profiler::endInterval(const std::string_view& name) {
std::scoped_lock lock(timerLock);
intervals[name].end = {getCurrentTimeNanoseconds()};
order[lastOrder++] = name;
}
void Profiler::profilerPoint() {
}
void Profiler::profilerPointCyclic() {
}
}

6
src/blt/std/system.cpp Normal file
View File

@ -0,0 +1,6 @@
/*
* Created by Brett on 04/01/23.
* Licensed under GNU General Public License V3.0
* See LICENSE file for license detail
*/
#include <blt/std/system.h>