Skip to content

Commit

Permalink
Optimisations to Text class, first constexpr paths
Browse files Browse the repository at this point in the history
~5-10% perf improvement.
  • Loading branch information
rthrfrd committed Dec 12, 2023
1 parent f738bcf commit 40b99f3
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 84 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,17 +426,17 @@ Running build/test/benchmark/webxx_benchmark
--------------------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------------------
singleElementInja 7689 ns 7577 ns 90478
singleElementWebxx 242 ns 242 ns 2601534
singleElementSprintf 77.0 ns 76.9 ns 8749016
singleElementStringAppend 27.8 ns 27.7 ns 25045977
multiElementInja 9819 ns 9800 ns 65152
multiElementWebxx 1077 ns 1071 ns 626847
multiElementSprintf 191 ns 189 ns 3580471
multiElementStringAppend 223 ns 222 ns 2990494
loop1kInja 1586433 ns 1583000 ns 417
loop1kWebxx 954590 ns 952975 ns 667
loop1kStringAppend 112652 ns 112369 ns 5784
singleElementInja 6442 ns 6438 ns 85931
singleElementWebxx 228 ns 227 ns 2939620
singleElementSprintf 70.3 ns 70.2 ns 9009705
singleElementStringAppend 26.8 ns 26.8 ns 25017959
multiElementInja 9208 ns 9206 ns 65686
multiElementWebxx 990 ns 990 ns 640756
multiElementSprintf 177 ns 176 ns 3711972
multiElementStringAppend 224 ns 224 ns 2844603
loop1kInja 1456063 ns 1454982 ns 455
loop1kWebxx 871208 ns 870924 ns 656
loop1kStringAppend 108399 ns 108362 ns 5607
# gcc-13 on macOS Ventura:
Running build/test/benchmark/webxx_benchmark
Expand Down
131 changes: 67 additions & 64 deletions include/webxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <cstring>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <typeinfo>
Expand Down Expand Up @@ -122,61 +123,63 @@ namespace Webxx { namespace internal {
PLACEHOLDER = 1,
};

struct Data {
std::string own;
std::string_view view;
Type type;

WEBXX_MOVE_ONLY_CONSTRUCTORS(Data)

Data (
const std::string& tOwn = none,
std::string_view tView = none,
Type tType = Type::LITERAL
) :
own{tOwn},
view{tView},
type{tType}
{}
};

mutable Data data;
Type type;
mutable std::optional<std::string> data;
std::string_view view;

Text (Text&&) = default; // move construct
Text& operator= (Text&&) = default; // move assign
Text (const Text& other) : data{ // copy construct
std::move(other.data)
} {}
Text (Text&& other) : // move construct
type{other.type},
data{std::move(other.data)},
view{data ? *data : other.view}
{}
Text& operator= (Text&& other) { // move assign
this->type = other.type;
this->data = std::move(other.data);
this->view = this->data ? *(this->data) : other.view;
return *this;
}
Text (const Text& other) : // copy construct
type{other.type},
data{std::move(other.data)},
view{data ? *data : other.view}
{}
Text& operator= (Text& other) { // copy assign
this->type = other.type;
this->data = std::move(other.data);
this->view = this->data ? *(this->data) : other.view;
return *this;
}

Text () : data {
} {}
Text (std::string&& value) : data {
std::move(value) // own
} {}
Text (const char* const value) : data {
{},
value, // view
} {}
Text (const std::string& value) : data {
value // own
} {}
Text (const std::string_view value) : data {
{},
std::move(value) // view
} {}
Text (Placeholder&& tPlaceholder) : data {
std::move(tPlaceholder), // own
{},
Text::Type::PLACEHOLDER
} {}

inline std::string_view getView () const {
return this->data.view.empty() ? std::string_view(this->data.own) : this->data.view;
}
constexpr Text () : // empty
type{Type::LITERAL},
data{},
view{none}
{}
Text (std::string&& value) : // own
type{Type::LITERAL},
data{std::move(value)},
view{*data}
{}
constexpr Text (const char* const value) : // view
type{Type::LITERAL},
data{},
view{value}
{}
Text (const std::string& value) : // own
type{Type::LITERAL},
data{value},
view{*data}
{}
Text (const std::string_view value) : // view
type{Type::LITERAL},
data{},
view{value}
{}
Text (Placeholder&& tPlaceholder) : // own
type{Type::PLACEHOLDER},
data{std::move(tPlaceholder)},
view{*data}
{}
};
}}

Expand Down Expand Up @@ -397,12 +400,12 @@ namespace Webxx { namespace internal {

mutable Data data;

HtmlAttribute (HtmlAttribute&&) = default; // move construct
HtmlAttribute& operator= (HtmlAttribute&&) = default; // move assign
HtmlAttribute (const HtmlAttribute& other) : data { // copy construct
std::move(other.data)
} {}
HtmlAttribute& operator= (HtmlAttribute& other) { // copy assign
HtmlAttribute (HtmlAttribute&&) = default; // move construct
HtmlAttribute& operator= (HtmlAttribute&&) = default; // move assign
HtmlAttribute (const HtmlAttribute& other) : // copy construct
data {std::move(other.data)}
{}
HtmlAttribute& operator= (HtmlAttribute& other) { // copy assign
this->data = std::move(other.data);
return *this;
}
Expand Down Expand Up @@ -910,12 +913,12 @@ namespace Webxx { namespace internal {
sendToRender(" ");
}

switch (value.data.type) {
switch (value.type) {
case Text::Type::LITERAL:
sendToRender(value.getView());
sendToRender(value.view);
break;
case Text::Type::PLACEHOLDER:
sendToRender(options.placeholderPopulator(value.getView(), attribute.data.name));
sendToRender(options.placeholderPopulator(value.view, attribute.data.name));
break;
}

Expand Down Expand Up @@ -972,9 +975,9 @@ namespace Webxx { namespace internal {
}

if (node.data.options.gathersCollection == PLACEHOLDER) {
sendToRender(options.placeholderPopulator(node.data.content.getView(), node.data.options.tagName));
sendToRender(options.placeholderPopulator(node.data.content.view, node.data.options.tagName));
} else {
sendToRender(node.data.content.getView());
sendToRender(node.data.content.view);
}

if (!node.data.children.empty()) {
Expand Down Expand Up @@ -1019,7 +1022,7 @@ namespace Webxx { namespace internal {
sendToRender(",");
}

sendToRender(selector.getView());
sendToRender(selector.view);

if (currentComponent) {
sendToRender("[");
Expand All @@ -1038,7 +1041,7 @@ namespace Webxx { namespace internal {
sendToRender(",");
}

sendToRender(selector.getView());
sendToRender(selector.view);

if (currentComponent) {
sendToRender("[");
Expand All @@ -1058,9 +1061,9 @@ namespace Webxx { namespace internal {
sendToRender(" ");
render(rule.data.selectors, 0);
}
if (!rule.data.value.getView().empty()) {
if (!rule.data.value.view.empty()) {
sendToRender(":");
sendToRender(rule.data.value.getView());
sendToRender(rule.data.value.view);
}
sendToRender(";");
} else {
Expand Down
4 changes: 2 additions & 2 deletions test/unit/Css.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ TEST_SUITE("CSS Stylesheet") {
color property{"red"};

CHECK(std::string(property.data.label) == "color");
CHECK(property.data.value.getView() == "red");
CHECK(property.data.value.view == "red");

SUBCASE("Property with value can be rendered") {
CHECK(render(property) == "color:red;");
Expand All @@ -22,7 +22,7 @@ TEST_SUITE("CSS Stylesheet") {
webkitSomething property{"foo"};

CHECK(property.data.label == webkitSomethingName);
CHECK(property.data.value.getView() == "foo");
CHECK(property.data.value.view == "foo");

SUBCASE("Customer property can be rendered") {
CHECK(render(property) == "-webkit-something:foo;");
Expand Down
14 changes: 7 additions & 7 deletions test/unit/Html.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ TEST_SUITE("Attribute") {
_class attribute{"big"};

CHECK(std::string(attribute.data.name) == "class");
CHECK(attribute.data.values[0].getView() == "big");
CHECK(attribute.data.values[0].view == "big");

SUBCASE("String literal attribute can be rendered") {
std::string rendered = render(attribute);
Expand All @@ -37,7 +37,7 @@ TEST_SUITE("Attribute") {
_class attribute{value};

CHECK(std::string(attribute.data.name) == "class");
CHECK(attribute.data.values[0].getView() == value);
CHECK(attribute.data.values[0].view == value);

SUBCASE("Const char attribute can be rendered") {
std::string rendered = render(attribute);
Expand All @@ -50,7 +50,7 @@ TEST_SUITE("Attribute") {
_class attribute{value};

CHECK(std::string(attribute.data.name) == "class");
CHECK(attribute.data.values[0].getView() == value);
CHECK(attribute.data.values[0].view == value);

SUBCASE("std::string attribute can be rendered") {
std::string rendered = render(attribute);
Expand All @@ -63,7 +63,7 @@ TEST_SUITE("Attribute") {
_class attribute{_{value}};

CHECK(std::string(attribute.data.name) == "class");
CHECK(attribute.data.values[0].getView() == value);
CHECK(attribute.data.values[0].view == value);

SUBCASE("Placeholder attribute can be rendered") {
std::string rendered = render(attribute);
Expand All @@ -75,8 +75,8 @@ TEST_SUITE("Attribute") {
_class attribute{"big", "tall"};

CHECK(std::string(attribute.data.name) == "class");
CHECK(attribute.data.values[0].getView() == "big");
CHECK(attribute.data.values[1].getView() == "tall");
CHECK(attribute.data.values[0].view == "big");
CHECK(attribute.data.values[1].view == "tall");

SUBCASE("Multiple values attribute can be rendered") {
std::string rendered = render(attribute);
Expand All @@ -88,7 +88,7 @@ TEST_SUITE("Attribute") {
_dataCustom customAttribute{"something"};

CHECK(std::string(customAttribute.data.name) == "data-custom");
CHECK(customAttribute.data.values[0].getView() == "something");
CHECK(customAttribute.data.values[0].view == "something");

SUBCASE("Custom attribute can be rendered") {
std::string rendered = render(customAttribute);
Expand Down

0 comments on commit 40b99f3

Please sign in to comment.