Skip to content

Commit

Permalink
readme + profile changes
Browse files Browse the repository at this point in the history
  • Loading branch information
TheNumbat committed Jan 7, 2024
1 parent b7a2575 commit fac3e2a
Show file tree
Hide file tree
Showing 23 changed files with 1,153 additions and 1,156 deletions.
4 changes: 0 additions & 4 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ jobs:
build-windows:
runs-on: windows-latest
steps:
- name: Stop Converting to CRLF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checkout
uses: actions/checkout@v4
- name: Configure
Expand Down
284 changes: 209 additions & 75 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,79 +3,213 @@
[![Windows](https://github.com/TheNumbat/rpp/actions/workflows/windows.yml/badge.svg?branch=main)](https://github.com/TheNumbat/rpp/actions/workflows/windows.yml)
[![Linux](https://github.com/TheNumbat/rpp/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/TheNumbat/rpp/actions/workflows/ubuntu.yml)

Minimal Rust-inspired C++20 standard library (mostly) replacement.

- Optimizes for fast compile times.
- Only includes code I'm actively using.

Headers
- base.h: reflection, allocators, profiling, strings, basic containers, basic math
- files.h: sync file IO
- net.h: sync network IO
- thread.h: threading primitives
- async.h: thread-safe coroutine primitives
- asyncio.h: IO coroutines
- pool.h: coroutine scheduler thread pool
- range_allocator.h: generic general purpose allocator
- simd.h: basic SSE vectors
- vmath.h: 3D vector and matrix math
- rng.h: hash-based PRNG
- function.h, heap.h, rc.h, stack.h, tuple.h, variant.h: see containers

Supported configurations
- Windows / x86_64 / MSVC 19.37+
- Linux / x86_64 / Clang 17+

Pointers
- Raw pointer: non owning
- Ref: non owning
- Box: owning unique
- Rc: refcount
- Arc: atomic refcount

Containers
- String: pascal style utf8 string
- String_View: non owning const string reference
- Array: fixed size array
- Vec: resizable array
- Slice: non owning const range reference
- Stack
- Queue: ring buffer
- Heap: linear priority queue
- Map: robin hood hash map
- Opt: in-place optional
- Storage: manual in-place RAII wrapper
- Pair: two values with heterogeneous types
- Tuple: N values with heterogeneous types
- Variant: sum type (only unique types) with scuffed pattern matching
- Function: fixed-size type-erased closure

Utilities
- 3D vector and matrix math (SSE)
- Type based allocator tracking
- Thread local region based stack allocator
- Timing and allocation profile tracking
- Various concepts
- Only trivial types are copyable
- Nontrivial types provide a clone method
- Generic reflection system
- Generic sprintf
- Hashing and PRNG
- Logging macros
- Custom coroutine wrappers
- Thread pool that schedules coroutines

To-Do:
- Update readme
Minimal Rust-inspired C++20 STL replacement.
Refer to the [blog post](https://thenumb.at/rpp/) for details.

## Integration

To use rpp in your project, run the following command (or manually download the source):

```bash
git submodule add https://github.com/TheNumbat/rpp
```

Then add the following lines to your CMakeLists.txt:

```cmake
add_subdirectory(rpp)
target_link_libraries($your_target PRIVATE rpp)
target_include_directories($your_target PRIVATE "rpp")
```

To use rpp with another build system, add `rpp` to your include path, add `rpp/rpp/impl/unify.cpp` to the build, and add either `rpp/rpp/pos/unify.cpp` or `rpp/rpp/w32/unify.cpp` based on your platform.

## Build and Run Tests

To build rpp and run the tests, run the following commands:

```bash
mkdir build
cd build
cmake ..
cmake --build .
ctest
```

For faster parallel builds, you can instead generate [ninja](https://ninja-build.org/) build files with `cmake -G Ninja ..`.

## Platform Support

Only the following configurations are supported:

| OS | Compiler | Arch |
|---------|-------------|------|
| Windows | MSVC 19.37+ | AVX2 |
| Linux | Clang 17+ | AVX2 |

Other configurations (macOS, aarch64, GCC, etc.) may be added in the future.

## Examples

### Logging

```cpp
#include <rpp/base.h>

i32 main() {
assert(true);
info("Information");
warn("Warning");
die("Fatal error (exits)");
}
```

### Data Structures

```cpp
#include <rpp/base.h>
#include <rpp/rc.h>
#include <rpp/stack.h>
#include <rpp/heap.h>
#include <rpp/tuple.h>
#include <rpp/variant.h>

using namespace rpp;

i32 main() {
Ref<i32> ref;
Box<i32, A> box;
Rc<i32, A> rc;
Arc<i32, A> arc;
Opt<i32> optional;
Storage<i32> storage;
String<A> string;
String_View string_view;
Array<i32, 1> array;
Vec<i32, A> vec;
Slice<i32, A> slice;
Stack<i32, A> stack;
Queue<i32, A> queue;
Heap<i32, A> heap;
Map<i32, i32> map;
Pair<i32, i32> pair;
Tuple<i32, i32, i32> tuple;
Variant<i32, f32> variant;
Function<i32()> function;
}
```

### Allocators

```cpp
#include <rpp/base.h>

using namespace rpp;

i32 main() {
using A = Mallocator<"A">;
using B = Mallocator<"B">;
{
Vec<i32, A> a;
Vec<i32, B> b;
info("A allocated: %", a);
info("B allocated: %", b);

Box<i32, Mpool> pool;
info("Pool allocated: %", pool);

Region(R) {
Vec<i32, Mregion<R>> region{1, 2, 3};
info("Region allocated: %", region);
}
}
Profile::finalize(); // Print statistics and check for leaks
}
```

### Reflection

```cpp
#include <rpp/base.h>

using namespace rpp;

struct Foo {
i32 x;
Vec<i32> y;
};
RPP_RECORD(Foo, RPP_FIELD(x), RPP_FIELD(y));

template<Reflectable T>
struct Bar {
T t;
};
template<Reflectable T>
RPP_TEMPLATE_RECORD(Bar, T, RPP_FIELD(t));

i32 main() {
Bar<Foo> bar{Foo{42, Vec<i32>{1, 2, 3}}};
info("bar: %", bar);
}
```
### Async
```cpp
#include <rpp/base.h>
#include <rpp/pool.h>
#include <rpp/asyncio.h>
using namespace rpp;
i32 main() {
Async::Pool<> pool;
auto coro = [](Async::Pool<>& pool) -> Async::Task<i32> {
co_await pool.suspend();
info("Hello from thread %!", Thread::this_id());
co_await AsyncIO::wait(pool, 100);
co_return 0;
};
auto task = coro(pool);
info("Task returned: %", task.block());
}
```

### Math

```cpp
#include <rpp/base.h>
#include <rpp/vmath.h>
#include <rpp/simd.h>

using namespace rpp;

i32 main() {
Vec3 v{0.0f, 1.0f, 0.0f};
Mat4 m = Mat4::translate(v);
info("Translated: %", m * v);

F32x8 simd = F32x8::set1(1.0f);
info("Dot product: %", F32x8::dot(simd, simd));
}
```

## To-Dos

- Modules
- Result<T,E> type
- Thread pool
- Priorities & CPU affinity
- Work stealing
- IO
- Proper async file IO on linux
- Map
- Don't store hashes given integer keys
- Range allocator
- Second level of linear buckets
- Reduce memory overhead
- Async
- [ ] scheduler priorities
- [ ] scheduler affinity
- [ ] scheduler work stealing
- [ ] io_uring for Linux file IO
- [ ] sockets
- Types
- [ ] Result<T,E>
- [ ] Map: don't store hashes of integer keys
- Allocators
- [ ] Per-thread pools
- Misc
- [ ] Range_Allocator: add second level of linear buckets
- [ ] Range_Allocator: reduce overhead
24 changes: 19 additions & 5 deletions rpp/impl/profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,24 @@ namespace rpp {
return static_cast<f32>(duration / static_cast<f64>(Thread::perf_frequency()));
}

void Profile::start_thread() noexcept {
void Profile::register_thread() noexcept {
Thread::Lock lock(threads_lock);

Thread::Id id = Thread::this_id();
assert(!threads.contains(id));

this_thread.registered = true;
this_thread.during_frame = false;
threads.insert(id, Ref{this_thread});
}

void Profile::end_thread() noexcept {
void Profile::unregister_thread() noexcept {
Thread::Lock lock(threads_lock);

Thread::Id id = Thread::this_id();
threads.erase(id);
static_cast<void>(threads.try_erase(id));

this_thread.~Thread_Profile();
this_thread.registered = false;
}

[[nodiscard]] Profile::Time_Point Profile::Frame_Profile::begin() noexcept {
Expand Down Expand Up @@ -60,6 +61,10 @@ void Profile::Frame_Profile::compute_self_times(u64 idx) noexcept {

f32 Profile::begin_frame() noexcept {

if(!this_thread.registered) return 0.0f;

assert(!this_thread.during_frame);

Thread_Profile& prof = this_thread;
Thread::Lock lock(prof.frames_lock);

Expand All @@ -84,6 +89,10 @@ f32 Profile::begin_frame() noexcept {

void Profile::end_frame() noexcept {

if(!this_thread.registered) return;

assert(this_thread.during_frame);

Thread_Profile& prof = this_thread;
Thread::Lock lock(prof.frames_lock);

Expand All @@ -97,12 +106,14 @@ void Profile::end_frame() noexcept {

void Profile::enter(String_View name) noexcept {
if constexpr(DO_PROFILE) {
if(!this_thread.ready()) return;
enter(Log::Location{move(name), ""_v, 0});
}
}

void Profile::enter(Log::Location loc) noexcept {
if constexpr(DO_PROFILE) {
if(!this_thread.ready()) return;
Thread::Lock lock(this_thread.frames_lock);
this_thread.frames.back().enter(move(loc));
}
Expand Down Expand Up @@ -134,6 +145,7 @@ void Profile::Frame_Profile::enter(Log::Location loc) noexcept {

void Profile::exit() noexcept {
if constexpr(DO_PROFILE) {
if(!this_thread.ready()) return;
Thread::Lock lock(this_thread.frames_lock);
this_thread.frames.back().exit();
}
Expand Down Expand Up @@ -196,14 +208,16 @@ void Profile::finalizer(Function<void()> f) noexcept {
}

void Profile::finalize() noexcept {
// All threads must have exited before we can finalize.
{
Thread::Lock lock(finalizers_lock);
for(auto& f : finalizers) {
f();
}
finalizers.~Vec();
}
end_thread();
unregister_thread();
this_thread.~Thread_Profile();
{
Thread::Lock lock(allocs_lock);
for(auto& prof : allocs) {
Expand Down
Loading

0 comments on commit fac3e2a

Please sign in to comment.