Compare commits
10 Commits
abc78334d0
...
bd8e17b2ab
| Author | SHA1 | Date | |
|---|---|---|---|
| bd8e17b2ab | |||
| 4d37acdda7 | |||
| 0509c62bf1 | |||
| b80ef30259 | |||
| df3ae69fb2 | |||
| 758115302a | |||
| 0f4a1871ad | |||
| 0c5f5aa464 | |||
| 6e0a576b2c | |||
| d2efae4a0a |
3
.clang-format
Normal file
3
.clang-format
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
ColumnLimit: 100
|
||||||
14
.clang-tidy
Normal file
14
.clang-tidy
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
Checks:
|
||||||
|
- boost-*
|
||||||
|
- bugprone-*
|
||||||
|
- clang-analyzer-*
|
||||||
|
- concurrency-*
|
||||||
|
- cppcoreguidelines-*
|
||||||
|
- misc-*
|
||||||
|
- modernize-*
|
||||||
|
- performance-*
|
||||||
|
- portability-*
|
||||||
|
- readability-*
|
||||||
|
- -readability-identifier-length
|
||||||
|
FormatStyle: file
|
||||||
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
### C++ ###
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
### clangd ###
|
||||||
|
.cache
|
||||||
|
|
||||||
|
### build ###
|
||||||
|
build/
|
||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
|||||||
[submodule "external/pipeline"]
|
[submodule "external/pipeline"]
|
||||||
path = external/pipeline
|
path = external/pipeline
|
||||||
url = https://github.com/p-ranav/pipeline.git
|
url = https://github.com/p-ranav/pipeline.git
|
||||||
|
[submodule "external/pipes"]
|
||||||
|
path = external/pipes
|
||||||
|
url = https://github.com/joboccara/pipes.git
|
||||||
|
|||||||
19
CMakeLists.txt
Normal file
19
CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20...4.0)
|
||||||
|
project(
|
||||||
|
pipe
|
||||||
|
VERSION 0.1.0
|
||||||
|
LANGUAGES CXX)
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
find_package(fmt REQUIRED)
|
||||||
|
add_subdirectory(external/pipeline)
|
||||||
|
add_subdirectory(external/pipes)
|
||||||
|
|
||||||
|
# Specify the include directories include_directories(include)
|
||||||
|
|
||||||
|
add_library(pipe INTERFACE)
|
||||||
|
target_compile_features(pipe INTERFACE cxx_std_23)
|
||||||
|
target_include_directories(pipe INTERFACE include)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(tests)
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Luís Murta
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
2
external/pipeline
vendored
2
external/pipeline
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 31fffe6af101da57513eebca7518b1e434f5de87
|
Subproject commit 9a6741d8a3ee819ec045cd8f14366189462f104a
|
||||||
1
external/pipes
vendored
Submodule
1
external/pipes
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit b91d984798765127b4325ef7206d1db42c528d24
|
||||||
72
include/freepipe/freepipe.hpp
Normal file
72
include/freepipe/freepipe.hpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2025, Luís Murta
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <any>
|
||||||
|
#include <concepts>
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include <ostream>
|
||||||
|
#include <tuple>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace freepipe {
|
||||||
|
|
||||||
|
template <typename... Args> struct Task {
|
||||||
|
explicit constexpr Task(Args &&...args)
|
||||||
|
requires(sizeof...(Args) > 0)
|
||||||
|
: args_(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
|
explicit constexpr Task()
|
||||||
|
requires(sizeof...(Args) == 0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
constexpr auto const &operator*() const & { return args_; }
|
||||||
|
constexpr auto &operator*() & { return args_; }
|
||||||
|
constexpr auto &&operator*() && { return std::move(args_); }
|
||||||
|
constexpr auto const &&operator*() const && { return std::move(args_); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::tuple<Args...> args_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Args, std::regular_invocable<Args...> F>
|
||||||
|
constexpr auto operator|(Task<Args...> const &task, F &&func) {
|
||||||
|
using R = std::invoke_result_t<F, Args...>;
|
||||||
|
|
||||||
|
if constexpr (std::is_void_v<R>) {
|
||||||
|
std::apply(std::forward<F>(func), *task);
|
||||||
|
return Task<>{};
|
||||||
|
} else {
|
||||||
|
return Task<R>{std::apply(std::forward<F>(func), *task)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args, std::regular_invocable<Args...> F>
|
||||||
|
constexpr auto operator|(Task<Args...> &&task, F &&func) {
|
||||||
|
using R = std::invoke_result_t<F, Args...>;
|
||||||
|
|
||||||
|
if constexpr (std::is_void_v<R>) {
|
||||||
|
std::apply(std::forward<F>(func), *std::move(task));
|
||||||
|
return Task<>{};
|
||||||
|
} else {
|
||||||
|
return Task<R>{std::apply(std::forward<F>(func), *std::move(task))};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args, typename CharT, typename Traits = std::char_traits<CharT>>
|
||||||
|
constexpr auto operator|(Task<Args...> const &task, std::basic_ostream<CharT, Traits> &os) {
|
||||||
|
std::apply([&os](auto &&...args) { ((os << args << '\n'), ...); }, *task);
|
||||||
|
return Task<>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args, typename CharT, typename Traits = std::char_traits<CharT>>
|
||||||
|
constexpr auto operator|(Task<Args...> &&task, std::basic_ostream<CharT, Traits> &os) {
|
||||||
|
std::apply([&os](auto &&...args) { ((os << args << '\n'), ...); }, *std::move(task));
|
||||||
|
return Task<>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pipe : public Task<> {};
|
||||||
|
|
||||||
|
} // namespace freepipe
|
||||||
3
tests/.clang-tidy
Normal file
3
tests/.clang-tidy
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
InheritParentConfig: true
|
||||||
|
Checks:
|
||||||
|
- -clang-analyzer-deadcode.DeadStores
|
||||||
12
tests/CMakeLists.txt
Normal file
12
tests/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
find_package(benchmark REQUIRED)
|
||||||
|
|
||||||
|
add_executable(pipeline.test pipeline.test.cpp)
|
||||||
|
target_compile_features(pipeline.test PUBLIC cxx_std_23)
|
||||||
|
target_link_libraries(pipeline.test benchmark::benchmark_main
|
||||||
|
pipeline::pipeline joboccara::pipes pipe)
|
||||||
|
add_test(NAME pipeline.test COMMAND pipeline.test)
|
||||||
|
|
||||||
|
find_package(GTest REQUIRED)
|
||||||
|
add_executable(pipe.test pipe.test.cpp)
|
||||||
|
target_compile_features(pipe.test PUBLIC cxx_std_23)
|
||||||
|
target_link_libraries(pipe.test GTest::gtest_main pipe)
|
||||||
25
tests/pipe.test.cpp
Normal file
25
tests/pipe.test.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include <freepipe/freepipe.hpp>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace freepipe {
|
||||||
|
|
||||||
|
TEST(PipeTest, Single) {
|
||||||
|
Pipe p;
|
||||||
|
|
||||||
|
auto r = p | [] { return 42; } |
|
||||||
|
[](auto result) {
|
||||||
|
EXPECT_EQ(result, 42);
|
||||||
|
return result;
|
||||||
|
} |
|
||||||
|
std::cout |
|
||||||
|
[] {
|
||||||
|
SUCCEED();
|
||||||
|
return 0;
|
||||||
|
} |
|
||||||
|
[](auto result) { EXPECT_EQ(result, 0); } | [] { return 0; };
|
||||||
|
|
||||||
|
EXPECT_EQ(*r, std::tuple{0});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace freepipe
|
||||||
78
tests/pipeline.test.cpp
Normal file
78
tests/pipeline.test.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
|
#include <freepipe/freepipe.hpp>
|
||||||
|
#include <pipeline/pipeline.hpp>
|
||||||
|
#include <pipes/pipes.hpp>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void BM_Pipeline(benchmark::State& state) {
|
||||||
|
using namespace pipeline;
|
||||||
|
|
||||||
|
for (auto _ : state) {
|
||||||
|
auto add = fn([](auto a, auto b) { return a + b; });
|
||||||
|
auto double_it = fn([](auto a) { return a * 2; });
|
||||||
|
auto square_it = fn([](auto a) { return a * a; });
|
||||||
|
auto pipeline = add | double_it | square_it;
|
||||||
|
|
||||||
|
auto ans = pipeline(0, 1);
|
||||||
|
benchmark::DoNotOptimize(ans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_Pipeline);
|
||||||
|
|
||||||
|
void BM_Pipes(benchmark::State& state) {
|
||||||
|
using namespace pipes;
|
||||||
|
|
||||||
|
for (auto _ : state) {
|
||||||
|
std::vector<int> ans;
|
||||||
|
|
||||||
|
mux(std::vector<int>{0}, std::vector<int>{1}) >>=
|
||||||
|
transform([](int a, int b) { return a + b; }) >>=
|
||||||
|
transform([](int a) { return a * 2; }) >>=
|
||||||
|
transform([](int a) { return a * a; }) >>= push_back(ans);
|
||||||
|
|
||||||
|
benchmark::DoNotOptimize(ans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_Pipes);
|
||||||
|
|
||||||
|
void BM_Pipe(benchmark::State &state) {
|
||||||
|
using namespace freepipe;
|
||||||
|
|
||||||
|
for (auto _ : state) {
|
||||||
|
const Pipe p;
|
||||||
|
|
||||||
|
auto r = p | [] { return std::tuple{0, 1}; } | [](auto ab) {
|
||||||
|
auto a = std::get<0>(ab);
|
||||||
|
auto b = std::get<1>(ab);
|
||||||
|
|
||||||
|
return a + b;
|
||||||
|
} | [](auto a) {
|
||||||
|
return a * 2;
|
||||||
|
} | [](auto a) { return a * a; };
|
||||||
|
|
||||||
|
benchmark::DoNotOptimize(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_Pipe);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// 2025-08-06T21:13:50+01:00
|
||||||
|
// Running /home/murta/src/pipeline/build/tests/pipeline.test
|
||||||
|
// Run on (16 X 3199.99 MHz CPU s)
|
||||||
|
// CPU Caches:
|
||||||
|
// L1 Data 32 KiB (x8)
|
||||||
|
// L1 Instruction 64 KiB (x8)
|
||||||
|
// L2 Unified 512 KiB (x8)
|
||||||
|
// L3 Unified 8192 KiB (x2)
|
||||||
|
// Load Average: 1.68, 1.49, 0.97
|
||||||
|
// -------------------------------------------------------
|
||||||
|
// Benchmark Time CPU Iterations
|
||||||
|
// -------------------------------------------------------
|
||||||
|
// BM_Pipeline 0.323 ns 0.323 ns 2163778209
|
||||||
|
// BM_Pipes 12.2 ns 12.2 ns 57225038
|
||||||
|
// BM_Pipe 0.320 ns 0.320 ns 2173384108
|
||||||
Loading…
x
Reference in New Issue
Block a user