Skip to content

Commit

Permalink
[DF] Speedup RInterface::Display
Browse files Browse the repository at this point in the history
The RDisplay implementation was accumulating code to JIT at every event in order
to retrieve a string representation for the value(s) to be displayed at that
event, via `cling::printValue`. For simple types such as arithmetic types and
1-dim collections thereof, we can avoid jitting by providing appropriate
overloads of the method that computes the string representation(s).

Co-authored-by: Vincenzo Eduardo Padulano <vincenzo.eduardo.padulano@cern.ch>
  • Loading branch information
ianna and vepadulano committed Feb 24, 2024
1 parent 7c5a37c commit dd35cc5
Showing 1 changed file with 88 additions and 26 deletions.
114 changes: 88 additions & 26 deletions tree/dataframe/inc/ROOT/RDF/RDisplay.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <vector>
#include <string>
#include <sstream>
#include <type_traits>

namespace ROOT {
namespace Internal {
Expand Down Expand Up @@ -90,17 +91,51 @@ private:
size_t fNMaxCollectionElements = 10; // threshold on number of elements in collections to be Print()

////////////////////////////////////////////////////////////////////////////
/// Appends a cling::printValue call to the stringstream.
/// Appends a cling::printValue call to the stringstream
/// This overload works for non-collection data types which are also not
/// trivially representable as strings.
/// \tparam T the type of the event to convert
/// \param[in] stream Where the conversion function call will be chained.
/// \param[in] element The event to convert to its string representation
/// \param[in] index To which column the event belongs to
/// \return false, the event is not a collection
template <typename T, std::enable_if_t<!ROOT::Internal::RDF::IsDataContainer<T>::value, int> = 0>
template <typename T,
std::enable_if_t<!std::is_arithmetic_v<T> && !ROOT::Internal::RDF::IsDataContainer<T>::value, int> = 0>
bool AddInterpreterString(std::stringstream &stream, T &element, const int &index)
{
stream << "*((std::string*)" << ROOT::Internal::RDF::PrettyPrintAddr(&(fRepresentations[index]))
<< ") = cling::printValue((" << fTypes[index] << "*)" << ROOT::Internal::RDF::PrettyPrintAddr(&element) << ");";
<< ") = cling::printValue((" << fTypes[index] << "*)" << ROOT::Internal::RDF::PrettyPrintAddr(&element)
<< ");";
return false;
}

////////////////////////////////////////////////////////////////////////////
/// Appends a string if the T type is an arithmetic type.
/// This overload works for arithmetic data types that are trivially
/// convertible to string.
/// \tparam T the type of the event to convert
/// \param[in] element The event to convert to its string representation
/// \param[in] index To which column the event belongs to
/// \return false, the event is not a collection
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
bool AddInterpreterString(std::stringstream &, T &element, const int &index)
{
// Short-circuit the logic and just insert the string representation of
// the symple type at the right index.
fRepresentations[index] = std::to_string(element);
return false;
}

////////////////////////////////////////////////////////////////////////////
/// Appends a string if the T type is boolean.
/// \param[in] element The event to convert to its string representation
/// \param[in] index To which column the event belongs to
/// \return false, the event is not a collection
bool AddInterpreterString(std::stringstream &, bool &element, const int &index)
{
// Short-circuit the logic and just insert the string representation of
// the boolean value at the right index.
fRepresentations[index] = (element ? "true" : "false");
return false;
}

Expand All @@ -112,7 +147,9 @@ private:
/// \param[in] index To which column the event belongs to
/// \return true, the event is a collection
/// This function chains a sequence of call to cling::printValue, one for each element of the collection.
template <typename T, std::enable_if_t<ROOT::Internal::RDF::IsDataContainer<T>::value, int> = 0>
template <typename T, std::enable_if_t<ROOT::Internal::RDF::IsDataContainer<T>::value &&
!std::is_arithmetic_v<typename T::value_type>,
int> = 0>
bool AddInterpreterString(std::stringstream &stream, T &collection, const int &index)
{
size_t collectionSize = std::distance(std::begin(collection), std::end(collection));
Expand All @@ -134,6 +171,53 @@ private:
return true;
}

////////////////////////////////////////////////////////////////////////////
/// Represent a collection of values as a collection of strings.
/// \tparam T the type of the event to convert. This must be a collection of
/// values of arithmetic type, but not boolean.
/// \param[in] collection The event to convert to its string representation
/// \param[in] index To which column the event belongs to
/// \return true, the event is a collection
template <typename T, std::enable_if_t<ROOT::Internal::RDF::IsDataContainer<T>::value &&
std::is_arithmetic_v<typename T::value_type> &&
!std::is_same_v<typename T::value_type, bool>,
int> = 0>
bool AddInterpreterString(std::stringstream &, T &collection, const int &index)
{
auto collectionSize = std::distance(std::begin(collection), std::end(collection));
VecStr_t collectionRepr(collectionSize);
std::generate(collectionRepr.begin(), collectionRepr.end(), [i = 0, &collection]() mutable {
auto valRepr = std::to_string(collection[i]);
i++;
return valRepr;
});
fCollectionsRepresentations[index] = std::move(collectionRepr);
return true;
}

////////////////////////////////////////////////////////////////////////////
/// Represent a collection of booleans as a collection of strings.
/// \tparam T the type of the event to convert. This must be a collection of
/// boolean values.
/// \param[in] collection The event to convert to its string representation
/// \param[in] index To which column the event belongs to
/// \return true, the event is a collection
template <typename T, std::enable_if_t<ROOT::Internal::RDF::IsDataContainer<T>::value &&
std::is_same_v<typename T::value_type, bool>,
int> = 0>
bool AddInterpreterString(std::stringstream &, T &collection, const int &index)
{
auto collectionSize = std::distance(std::begin(collection), std::end(collection));
VecStr_t collectionRepr(collectionSize);
std::generate(collectionRepr.begin(), collectionRepr.end(), [i = 0, &collection]() mutable {
auto valRepr = (collection[i] ? "true" : "false");
i++;
return valRepr;
});
fCollectionsRepresentations[index] = std::move(collectionRepr);
return true;
}

////////////////////////////////////////////////////////////////////////////
/// AddInterpreterString overload for arrays of chars.
///
Expand All @@ -153,28 +237,6 @@ private:
return false; // do not treat this as a collection
}

////////////////////////////////////////////////////////////////////////////
/// AddInterpreterString overload for arrays of booleans.
///
/// \param[in] boolArr The bool array to convert to string representation
/// \param[in] index To which column the event belongs
/// \return true, the event is a collection
///
/// This specialization for arrays of booleans skips the cling::printValue
/// (i.e. appends nothing to the stream) and directly writes to fCollectionsRepresentations the
/// string representation of the array of chars.
bool AddInterpreterString(std::stringstream &, ROOT::RVec<bool> &boolArr, const int &index)
{
auto &collRepr = fCollectionsRepresentations[index];
collRepr.clear();
collRepr.reserve(boolArr.size());
for (bool b : boolArr)
collRepr.push_back(b ? "true" : "false");

return true; // treat this as a collection
}


////////////////////////////////////////////////////////////////////////////
/// Adds a single element to the next slot in the table
void AddToRow(const std::string &stringEle);
Expand Down

0 comments on commit dd35cc5

Please sign in to comment.