Skip to content

Commit 466a9c2

Browse files
committed
Fixes performance bottleneck in subset_map/extend
You can expect to see a 2x performance increase in bigger sass code bases. With more extend usage it can go as high as 10x performance increase.
1 parent ff449dc commit 466a9c2

17 files changed

+373
-250
lines changed

Makefile.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ SOURCES = \
4343
to_c.cpp \
4444
to_value.cpp \
4545
source_map.cpp \
46+
subset_map.cpp \
4647
error_handling.cpp \
4748
memory/SharedPtr.cpp \
4849
utf8_string.cpp \

src/ast.cpp

Lines changed: 119 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ namespace Sass {
3030
SASS_MEMORY_CAST(Supports_Operator, cond);
3131
}
3232

33+
size_t HashExpression::operator() (Expression_Obj ex) const {
34+
return ex ? ex->hash() : 0;
35+
}
36+
37+
size_t HashSimpleSelector::operator() (Simple_Selector_Obj ex) const {
38+
return ex ? ex->hash() : 0;
39+
}
40+
41+
42+
bool CompareExpression::operator()(const Expression_Obj& lhs, const Expression_Obj& rhs) const {
43+
return lhs && rhs && lhs->eq(*rhs);
44+
}
45+
46+
bool CompareSimpleSelector::operator()(Simple_Selector_Obj lhs, Simple_Selector_Obj rhs) const {
47+
return &lhs && &rhs && *lhs == *rhs;
48+
}
49+
3350
std::string & str_ltrim(std::string & str)
3451
{
3552
auto it2 = std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
@@ -286,11 +303,11 @@ namespace Sass {
286303
l_h = l ? &l->head() : 0;
287304
r_h = r ? &r->head() : 0;
288305
}
289-
// fail if only one is null
290-
else if (!r_h) return !l_h;
291-
else if (!l_h) return !r_h;
292-
// heads ok and equal
293-
else if (*l_h == *r_h)
306+
// equals if other head is empty
307+
else if ((!l_h && !r_h) ||
308+
(!l_h && r_h->empty()) ||
309+
(!r_h && l_h->empty()) ||
310+
(*l_h == *r_h))
294311
{
295312
// check combinator after heads
296313
if (l->combinator() != r->combinator())
@@ -321,24 +338,68 @@ namespace Sass {
321338
return unified.detach();
322339
}
323340

341+
bool Selector::operator== (const Selector& rhs) const
342+
{
343+
if (Selector_List_Ptr_Const sl = dynamic_cast<Selector_List_Ptr_Const>(this)) return *sl == rhs;
344+
if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(this)) return *sp == rhs;
345+
throw "invalid selector base classes to compare";
346+
return false;
347+
}
348+
349+
bool Selector::operator< (const Selector& rhs) const
350+
{
351+
if (Selector_List_Ptr_Const sl = dynamic_cast<Selector_List_Ptr_Const>(this)) return *sl < rhs;
352+
if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(this)) return *sp < rhs;
353+
throw "invalid selector base classes to compare";
354+
return false;
355+
}
356+
357+
bool Simple_Selector::operator== (const Selector& rhs) const
358+
{
359+
if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(&rhs)) return *this == *sp;
360+
return false;
361+
}
362+
363+
bool Simple_Selector::operator< (const Selector& rhs) const
364+
{
365+
if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(&rhs)) return *this < *sp;
366+
return false;
367+
}
368+
324369
bool Simple_Selector::operator== (const Simple_Selector& rhs) const
325370
{
326-
if (Pseudo_Selector_Ptr_Const lp = dynamic_cast<Pseudo_Selector_Ptr_Const>(this)) return *lp == rhs;
327-
if (Wrapped_Selector_Ptr_Const lw = dynamic_cast<Wrapped_Selector_Ptr_Const>(this)) return *lw == rhs;
328-
if (Attribute_Selector_Ptr_Const la = dynamic_cast<Attribute_Selector_Ptr_Const>(this)) return *la == rhs;
329-
if (is_ns_eq(ns(), rhs.ns()))
330-
{ return name() == rhs.name(); }
331-
return ns() == rhs.ns();
371+
Simple_Type type = simple_type();
372+
// dynamic cast is a bottleneck - use concrete type as types are final
373+
if (type == PSEUDO_SEL /* Pseudo_Selector_Ptr_Const lp = dynamic_cast<Pseudo_Selector_Ptr_Const>(this) */) {
374+
return *static_cast<Pseudo_Selector_Ptr_Const>(this) == rhs;
375+
}
376+
else if (type == WRAPPED_SEL /* Wrapped_Selector_Ptr_Const lw = dynamic_cast<Wrapped_Selector_Ptr_Const>(this) */) {
377+
return *static_cast<Wrapped_Selector_Ptr_Const>(this) == rhs;
378+
}
379+
else if (type == ATTR_SEL /* Attribute_Selector_Ptr_Const la = dynamic_cast<Attribute_Selector_Ptr_Const>(this) */) {
380+
return *static_cast<Attribute_Selector_Ptr_Const>(this) == rhs;
381+
}
382+
else if (name_ == rhs.name_)
383+
{ return is_ns_eq(ns_, rhs.ns_); }
384+
else return false;
332385
}
333386

334387
bool Simple_Selector::operator< (const Simple_Selector& rhs) const
335388
{
336-
if (Pseudo_Selector_Ptr_Const lp = dynamic_cast<Pseudo_Selector_Ptr_Const>(this)) return *lp == rhs;
337-
if (Wrapped_Selector_Ptr_Const lw = dynamic_cast<Wrapped_Selector_Ptr_Const>(this)) return *lw < rhs;
338-
if (Attribute_Selector_Ptr_Const la = dynamic_cast<Attribute_Selector_Ptr_Const>(this)) return *la < rhs;
339-
if (is_ns_eq(ns(), rhs.ns()))
340-
{ return name() < rhs.name(); }
341-
return ns() < rhs.ns();
389+
Simple_Type type = simple_type();
390+
// dynamic cast is a bottleneck - use concrete type as types are final
391+
if (type == PSEUDO_SEL /* Pseudo_Selector_Ptr_Const lp = dynamic_cast<Pseudo_Selector_Ptr_Const>(this) */) {
392+
return *static_cast<Pseudo_Selector_Ptr_Const>(this) < rhs;
393+
}
394+
else if (type == WRAPPED_SEL /* Wrapped_Selector_Ptr_Const lw = dynamic_cast<Wrapped_Selector_Ptr_Const>(this) */) {
395+
return *static_cast<Wrapped_Selector_Ptr_Const>(this) < rhs;
396+
}
397+
else if (type == ATTR_SEL /* Attribute_Selector_Ptr_Const la = dynamic_cast<Attribute_Selector_Ptr_Const>(this) */) {
398+
return *static_cast<Attribute_Selector_Ptr_Const>(this) < rhs;
399+
}
400+
if (is_ns_eq(ns_, rhs.ns_))
401+
{ return name_ < rhs.name_; }
402+
return ns_ < rhs.ns_;
342403
}
343404

344405
bool Selector_List::operator== (const Selector& rhs) const
@@ -394,6 +455,21 @@ namespace Sass {
394455
return true;
395456
}
396457

458+
bool Selector_List::operator< (const Selector& rhs) const
459+
{
460+
if (Selector_List_Ptr_Const sp = dynamic_cast<Selector_List_Ptr_Const>(&rhs)) return *this < *sp;
461+
return false;
462+
}
463+
464+
bool Selector_List::operator< (const Selector_List& rhs) const
465+
{
466+
if (this->length() != rhs.length()) return false;
467+
for (size_t i = 0; i < rhs.length(); i ++) {
468+
if (!(*at(i) < *rhs.at(i))) return false;
469+
}
470+
return true;
471+
}
472+
397473
Compound_Selector_Ptr Simple_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)
398474
{
399475
for (size_t i = 0, L = rhs->length(); i < L; ++i)
@@ -539,12 +615,17 @@ namespace Sass {
539615

540616
bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const
541617
{
542-
543618
if (is_ns_eq(ns(), rhs.ns())) {
544619
if (name() == rhs.name()) {
545620
if (matcher() == rhs.matcher()) {
546-
return &value() && &rhs.value() &&
547-
*value() < *rhs.value();
621+
bool no_lhs_val = value().isNull();
622+
bool no_rhs_val = rhs.value().isNull();
623+
if (no_lhs_val && no_rhs_val) {
624+
return true;
625+
}
626+
if (!no_lhs_val && !no_rhs_val) {
627+
return *value() < *rhs.value();
628+
}
548629
} else { return matcher() < rhs.matcher(); }
549630
} else { return name() < rhs.name(); }
550631
}
@@ -564,11 +645,25 @@ namespace Sass {
564645

565646
bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const
566647
{
567-
return (name() == rhs.name())
648+
// get optional value state
649+
bool no_lhs_val = value().isNull();
650+
bool no_rhs_val = rhs.value().isNull();
651+
// both are null, therefore equal
652+
if (no_lhs_val && no_rhs_val) {
653+
return (name() == rhs.name())
654+
&& (matcher() == rhs.matcher())
655+
&& (is_ns_eq(ns(), rhs.ns()));
656+
}
657+
// both are defined, evaluate
658+
if (no_lhs_val == no_rhs_val) {
659+
return (name() == rhs.name())
568660
&& (matcher() == rhs.matcher())
569661
&& (is_ns_eq(ns(), rhs.ns()))
570-
&& (&value() && &rhs.value())
571662
&& (*value() == *rhs.value());
663+
}
664+
// not equal
665+
return false;
666+
572667
}
573668

574669
bool Attribute_Selector::operator== (const Simple_Selector& rhs) const
@@ -1471,7 +1566,7 @@ namespace Sass {
14711566
return final_result;
14721567
}
14731568

1474-
void Selector_List::populate_extends(Selector_List_Obj extendee, Context& ctx, ExtensionSubsetMap& extends)
1569+
void Selector_List::populate_extends(Selector_List_Obj extendee, Context& ctx, Subset_Map& extends)
14751570
{
14761571

14771572
Selector_List_Ptr extender = this;
@@ -1499,7 +1594,7 @@ namespace Sass {
14991594
compound_sel->is_optional(extendee->is_optional());
15001595

15011596
for (size_t i = 0, L = extender->length(); i < L; ++i) {
1502-
extends.put(compound_sel->to_str_vec(), std::make_pair(&(*extender)[i], &compound_sel));
1597+
extends.put(compound_sel, std::make_pair(&(*extender)[i], &compound_sel));
15031598
}
15041599
}
15051600
};

0 commit comments

Comments
 (0)