Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion phlex/core/framework_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ namespace phlex::experimental {
std::size_t framework_graph::seen_cell_count(std::string const& layer_name,
bool const missing_ok) const
{
return hierarchy_.count_for(layer_name, missing_ok);
return hierarchy_.count_for(experimental::layer_path(layer_name), missing_ok);
}

std::size_t framework_graph::execution_count(std::string const& node_name) const
Expand Down
67 changes: 22 additions & 45 deletions phlex/core/index_router.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "phlex/core/index_router.hpp"

#include "phlex/model/flush_gate.hpp"
#include "phlex/utilities/bulleted_list.hpp"
#include "phlex/utilities/hashing.hpp"

#include "fmt/std.h"
Expand All @@ -14,34 +15,6 @@

using namespace phlex::experimental;

namespace {
using layer_path_t = std::vector<std::string>;

std::size_t layer_hash_for_path(layer_path_t const& layer_path)
{
std::size_t result = "job"_id.hash();
for (auto const& layer_name : layer_path | std::views::drop(1)) {
result = hash(result, identifier{layer_name}.hash());
}
return result;
}

bool is_strict_prefix(layer_path_t const& candidate, layer_path_t const& other)
{
// FIXME: Use std::ranges::starts_with(other, candidate) once the compilers support it (C++23)
return candidate.size() < other.size() and
std::ranges::mismatch(other, candidate).in2 == std::ranges::end(candidate);
}

std::string delimited_layer_path(std::string_view const layer_path)
{
if (not layer_path.starts_with("/")) {
return fmt::format("/{}", layer_path);
}
return std::string{layer_path};
}
}

namespace phlex::experimental {

//========================================================================================
Expand All @@ -57,7 +30,7 @@ namespace phlex::experimental {
void put_message(data_cell_index_ptr const& index, std::size_t message_id);
void put_end_token(data_cell_index_ptr const& index, flush_gate const& fc);

bool matches_exactly(std::string const& layer_path) const;
bool matches_exactly(layer_path const& layer_path) const;
bool is_parent_of(data_cell_index_ptr const& index) const;

private:
Expand Down Expand Up @@ -93,9 +66,9 @@ namespace phlex::experimental {
flusher_.try_put({.index = index, .count = static_cast<int>(fc.committed_total_count())});
}

bool multilayer_slot::matches_exactly(std::string const& layer_path) const
bool multilayer_slot::matches_exactly(layer_path const& layer_path) const
{
return layer_path.ends_with(delimited_layer_path(static_cast<std::string_view>(layer_)));
return layer_path.ends_with(layer_);
}

bool multilayer_slot::is_parent_of(data_cell_index_ptr const& index) const
Expand Down Expand Up @@ -131,16 +104,22 @@ namespace phlex::experimental {
std::vector<identifier> unfold_input_layer_names,
std::vector<identifier> unfold_output_layer_names)
{
auto sorted_layer_paths = layer_paths_from_driver;
auto sorted_layer_paths =
layer_paths_from_driver | std::views::transform([](auto const& lp) {
auto lp_as_ids = lp |
std::views::transform([](auto const& str) { return identifier(str); }) |
std::ranges::to<std::vector>();
return layer_path(std::move(lp_as_ids));
}) |
std::ranges::to<std::vector<layer_path>>();
std::ranges::sort(sorted_layer_paths);

// In sorted order, a path can only be a prefix of paths that follow it.
for (std::size_t i = 0; i < sorted_layer_paths.size(); ++i) {
for (std::size_t i = 0; i + 1 < sorted_layer_paths.size(); ++i) {
bool const is_not_lowest_layer =
i + 1 < sorted_layer_paths.size() and
is_strict_prefix(sorted_layer_paths[i], sorted_layer_paths[i + 1]);
sorted_layer_paths[i].is_strict_prefix_of(sorted_layer_paths[i + 1]);
if (is_not_lowest_layer) {
auto const layer_hash = layer_hash_for_path(sorted_layer_paths[i]);
auto const layer_hash = sorted_layer_paths[i].hash();
is_lowest_layer_hashes_.emplace(layer_hash, false);
}
}
Expand Down Expand Up @@ -252,19 +231,17 @@ namespace phlex::experimental {
return it->second;
}

std::string const layerish_path{static_cast<std::string_view>(index->layer_name())};
layer_path const layerish_path{{index->layer_name()}};
auto broadcaster = index_set_node_for(layerish_path);
index_set_node_cache_.insert({layer_hash, broadcaster});
return broadcaster;
}

auto index_router::index_set_node_for(std::string const& layer_path) -> detail::index_set_node_ptr
auto index_router::index_set_node_for(layer_path const& layer_path) -> detail::index_set_node_ptr
{
std::string const search_token = delimited_layer_path(layer_path);

std::vector<decltype(index_set_nodes_.begin())> candidates;
for (auto it = index_set_nodes_.begin(), e = index_set_nodes_.end(); it != e; ++it) {
if (search_token.ends_with(delimited_layer_path(static_cast<std::string_view>(it->first)))) {
if (layer_path.ends_with(it->first)) {
candidates.push_back(it);
}
}
Expand All @@ -277,10 +254,10 @@ namespace phlex::experimental {
return nullptr;
}

std::string msg = fmt::format("Multiple layers match specification {}:\n", layer_path);
for (auto const& it : candidates) {
msg += fmt::format("\n- {}", it->first);
}
std::string msg = fmt::format(
"Multiple layers match specification {}:\n{}",
layer_path,
bulleted_list(candidates | std::views::transform([](auto const& it) { return it->first; })));
throw std::runtime_error(msg);
}

Expand Down
2 changes: 1 addition & 1 deletion phlex/core/index_router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ namespace phlex::experimental {
// correct for unfold outputs (the only source of unknown hashes) and consistent with
// index_is_lowest_layer()'s fall-through default.
bool is_lowest_layer_hash(std::size_t layer_hash) const;
detail::index_set_node_ptr index_set_node_for(std::string const& layer);
detail::index_set_node_ptr index_set_node_for(layer_path const& layer);
detail::index_set_node_ptr index_set_node_for(data_cell_index_ptr const& index);
std::pair<detail::multilayer_slots_ptr, detail::multilayer_slots_ptr> multilayer_slots_for(
data_cell_index_ptr const& index);
Expand Down
2 changes: 2 additions & 0 deletions phlex/model/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cet_make_library(
data_layer_hierarchy.cpp
data_cell_index.cpp
identifier.cpp
layer_path.cpp
product_matcher.cpp
product_store.cpp
products.cpp
Expand Down Expand Up @@ -39,6 +40,7 @@ install(
data_layer_hierarchy.hpp
data_cell_index.hpp
identifier.hpp
layer_path.hpp
product_matcher.hpp
product_specification.hpp
product_store.hpp
Expand Down
15 changes: 8 additions & 7 deletions phlex/model/data_cell_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,16 @@ namespace phlex {
return layer_name_;
}

std::string data_cell_index::layer_path() const
experimental::layer_path data_cell_index::layer_path() const
{
std::vector layers_in_reverse{std::string_view(layer_name_)};
auto next_parent = parent();
while (next_parent) {
layers_in_reverse.push_back(std::string_view(next_parent->layer_name()));
next_parent = next_parent->parent();
// We know how deep we are so we can pre-allocate and fill in reverse
std::vector<experimental::identifier> layers(depth_ + 1);
auto const* ptr = this;
for (auto& layer : std::views::reverse(layers)) {
layer = ptr->layer_name();
ptr = ptr->parent_.get();
}
return fmt::format("/{}", fmt::join(std::views::reverse(layers_in_reverse), "/"));
return experimental::layer_path{std::move(layers)};
}

std::size_t data_cell_index::depth() const noexcept { return depth_; }
Expand Down
3 changes: 2 additions & 1 deletion phlex/model/data_cell_index.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "phlex/model/fwd.hpp"
#include "phlex/model/identifier.hpp"
#include "phlex/model/layer_path.hpp"

#include <cstddef>
#include <initializer_list>
Expand All @@ -23,7 +24,7 @@ namespace phlex {
using hash_type = std::size_t;
data_cell_index_ptr make_child(std::string layer_name, std::size_t data_cell_number) const;
experimental::identifier const& layer_name() const noexcept;
std::string layer_path() const;
experimental::layer_path layer_path() const;
std::size_t depth() const noexcept;
data_cell_index_ptr parent(experimental::identifier const& layer_name) const;
data_cell_index_ptr parent() const noexcept;
Expand Down
27 changes: 14 additions & 13 deletions phlex/model/data_layer_hierarchy.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "phlex/model/data_layer_hierarchy.hpp"
#include "phlex/model/data_cell_index.hpp"

#include "phlex/utilities/bulleted_list.hpp"

#include "fmt/format.h"
#include "fmt/std.h"
#include "spdlog/spdlog.h"
Expand Down Expand Up @@ -35,32 +37,31 @@ namespace phlex::experimental {
++it->second->count;
}

std::size_t data_layer_hierarchy::count_for(std::string const& layer, bool const missing_ok) const
std::size_t data_layer_hierarchy::count_for(layer_path const& layer, bool const missing_ok) const
{
// The assumption is that specified layer is the component of a layer path
std::string search_token = layer;
if (not layer.starts_with("/")) {
search_token = '/' + layer;
}

std::vector<layer_entry const*> candidates;
for (auto const& [_, entry] : layers_) {
if (entry->layer_path.ends_with(search_token)) {
if (entry->layer_path.ends_with(layer)) {
candidates.push_back(entry.get());
}
}

if (candidates.empty()) {
return missing_ok ? 0ull
: throw std::runtime_error("No layers match the specification " + layer);
: throw std::runtime_error(
fmt::format("No layers match the specification {}", layer));
}

if (candidates.size() > 1ull) {
std::string msg{"The following data layers match the specification " + layer + ":\n"};
for (auto const* entry : candidates) {
msg += "\n- " + entry->layer_path;
}
msg += "\n\nPlease specify the full layer path to disambiguate between them.";
std::string msg =
fmt::format("The following data layers match the specification {}:\n\n{}"
"\n\nPlease specify the full layer path to disambiguate between them.",
layer,
bulleted_list(candidates | std::views::transform([](auto const* entry) {
return entry->layer_path;
}),
/*indent=*/0));
throw std::runtime_error(msg);
}

Expand Down
7 changes: 4 additions & 3 deletions phlex/model/data_layer_hierarchy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "phlex/model/data_cell_index.hpp"
#include "phlex/model/fwd.hpp"
#include "phlex/model/layer_path.hpp"

#include "oneapi/tbb/concurrent_unordered_map.h"

Expand All @@ -25,7 +26,7 @@ namespace phlex::experimental {
data_layer_hierarchy& operator=(data_layer_hierarchy&&) = delete;

void increment_count(data_cell_index_ptr const& id);
std::size_t count_for(std::string const& layer, bool missing_ok = false) const;
std::size_t count_for(layer_path const& layer, bool missing_ok = false) const;

void print() const;

Expand All @@ -39,13 +40,13 @@ namespace phlex::experimental {
std::string indent = {}) const;

struct layer_entry {
layer_entry(identifier n, std::string path, std::size_t par_hash) :
layer_entry(identifier n, experimental::layer_path path, std::size_t par_hash) :
name{std::move(n)}, layer_path{std::move(path)}, parent_hash{par_hash}
{
}

identifier name;
std::string layer_path;
experimental::layer_path layer_path;
std::size_t parent_hash;
std::atomic<std::size_t> count{};
};
Expand Down
2 changes: 1 addition & 1 deletion phlex/model/fixed_hierarchy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ namespace phlex {
return data_cell_cursor{child, hierarchy_, driver_};
}

std::string data_cell_cursor::layer_path() const { return index_->layer_path(); }
experimental::layer_path data_cell_cursor::layer_path() const { return index_->layer_path(); }

// ================================================================================
// data_cell_yielder implementation
Expand Down
3 changes: 2 additions & 1 deletion phlex/model/fixed_hierarchy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "phlex/phlex_model_export.hpp"

#include "phlex/model/fwd.hpp"
#include "phlex/model/layer_path.hpp"

#include <cstddef>
#include <initializer_list>
Expand All @@ -25,7 +26,7 @@ namespace phlex {
// data-cell index to the underlying driver, returning a data_cell_cursor for the child.
data_cell_cursor yield_child(std::string const& layer_name, std::size_t number) const;

std::string layer_path() const;
experimental::layer_path layer_path() const;

private:
friend class fixed_hierarchy;
Expand Down
2 changes: 1 addition & 1 deletion phlex/model/handle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ namespace phlex {
}
std::string_view suffix() const noexcept { return std::string_view(suffix_); }
std::string_view layer() const noexcept { return std::string_view(id_->layer_name()); }
std::string layer_path() const { return id_->layer_path(); }
std::string layer_path() const { return id_->layer_path().to_string(); }

template <typename U>
friend class handle;
Expand Down
1 change: 1 addition & 0 deletions phlex/model/identifier.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace phlex::experimental {

// Really trying to avoid the extra function call here
inline std::string_view format_as(identifier const& id) { return std::string_view(id); }
inline std::size_t hash_value(identifier const& id) { return id.hash(); }
}

template <>
Expand Down
61 changes: 61 additions & 0 deletions phlex/model/layer_path.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include "layer_path.hpp"

#include "boost/container_hash/hash.hpp"
#include "fmt/format.h"
#include "fmt/ranges.h"

#include <algorithm>
#include <ranges>

using namespace std::literals;
using namespace phlex::experimental::literals;

namespace phlex::experimental {
layer_path::layer_path(std::string_view path) :
layer_path_{
std::from_range,
path | std::views::split('/') |
std::views::filter([](auto const& sr) { return not sr.empty(); }) |
Comment thread
beojan marked this conversation as resolved.
std::views::transform([](auto const& sr) { return identifier(std::string_view(sr)); })}
{
if (path.starts_with("/") and not path.starts_with("/job")) {
Comment thread
beojan marked this conversation as resolved.
Outdated
throw std::runtime_error(
fmt::format("A complete layer path must start with '/job'. '{}' does not!", path));
}
}

bool layer_path::empty() const noexcept { return layer_path_.empty(); }

bool layer_path::complete() const noexcept { return not empty() and layer_path_[0] == "job"_idq; }

bool layer_path::is_strict_prefix_of(layer_path const& other) const noexcept
{
return std::ranges::starts_with(other.layer_path_, layer_path_);
Comment thread
beojan marked this conversation as resolved.
Outdated
}

bool layer_path::ends_with(layer_path const& other) const noexcept
{
return std::ranges::ends_with(layer_path_, other.layer_path_);
Comment thread
beojan marked this conversation as resolved.
Outdated
}
bool layer_path::ends_with(identifier const& name) const noexcept
{
return not empty() and layer_path_.back() == name;
}

std::string layer_path::to_string() const
{
return fmt::format("{}{}", complete() ? "/" : "", fmt::join(layer_path_, "/"));
}
std::size_t layer_path::hash() const noexcept
{
if (empty()) {
return 0;
}
if (layer_path_.size() == 1) {
return layer_path_[0].hash();
}
std::size_t seed = layer_path_[0].hash();
boost::hash_range(seed, layer_path_.begin() + 1, layer_path_.end());
return seed;
}
}
Loading
Loading