📜 ⬆️ ⬇️

YASS Architecture Part 3: the problem of choice

This is the third article from the series on the analysis of practical methods underlying YASS . The first article was about modular construction , the second - about the logic of choosing a CSS selector and the organization of cycles .

Conditional branching



Let's start with the most obvious component of any logic: branching. In any algorithm, there is a place where you need to choose a particular continuation depending on the condition being tested. Let's look at the following examples. In the first case, we have three simple nested checks:
')
  var a = 1,
	 b = 2,
	 c = 3;
 if (a == 1) {
	 if (b == 2) {
		 if (c == 3) {
			 ...
		 }
	 }
 } 


This, the most interesting, works as fast as the combined if :

  if (a == 1 && b == 2 && c == 3) {
	 ...
 } 


However, the latter is slightly smaller. If the task is not the minimum code size, then to improve readability, use the first option. If we minimize everything, then we can consider using the if-then-else expression. But you need to keep in mind that the performance of such structures:

  var v = a == 1?  b == 2?  c == 3?  1: 0: 0: 0; 


about 10-50% less than the usual branch, considered a little higher.

In the case when all our variables are numeric, then checking the equality of their sum given will be performed 5–10% faster:

  if (a + b + c == 6) {
	 ...
 } 


If we just need to check the existence of variables and their non-negativity (that is, the variables are not undefined , not NaN , not null , not '' and not 0), then the next option will work 5–10% faster, than the previous case (and 10–20% faster than the very first example):

  if (a && b && c) {
	 ...
 } 


Very often we need to check something more complicated than just a number. For example, the match of a string with a given or equality of objects. In this case, we just need the following comparison:

  var a = 1,
	 b = 2,
	 c = '3';
 if (a == 1 && b == 2 && c === '3') {
	 ...
 } 


Here we use the no-cast comparison === , which in the case of non-numeric variables is 10–20% faster than the usual comparison.

Selection depending on the string



Often enough, we need to select one of the conditional branches based on a given string. Usually, this is done using either the methods of the RegExp object ( exec , test ) or the string methods ( match , search , indexOf ). If we just need to check if the string matches a regular expression, then the test best suited for this:

  var str = 'abc',
	 regexp = new RegExp ('abc');
 if (regexp.test (str)) {
	 ...
 } 


Such a construction will work 40% faster than a similar exec :

  if (regexp.exec (str) [1]) {
	 ...
 } 


The string match method is similar to the exec method of the RegExp object being created, but it works 10–15% faster in the case of simple expressions. However, the search method is slightly slower (5–10%) than test , because the latter does not return the found substring.

In that case, if the regular expression is required “at one time”, then a faster (about 10% relative to the variant with initialization of the new object) is suitable:

  if (/abc/.test (str)) {
	 ...
 } 


If, finally, we need to check just finding the substring in the given string, then the indisputable leader will be exactly indexOf , which works 2 times faster than regular expressions parsing:

  if (str.indexOf ('abc')! = -1) {
	 ...
 } 


Exact match and hashes



Let's now consider the following version of a regular expression: /a|b|c/ . In this case, we need to check in the given string whether one of the possible options is present (or if the string is equal to this variant). In the case of an exact match, a regular expression faster (by 50%) will check the string as the key of a hash:

  var hash = {'a': 1, 'b': 1},
	 str = 'a';
 if (h [str]) {
	 ...
 } 


Faster (by 20%) of such a hash will only be an exact check of the string for certain values:

  if (str === 'a' || str === 'b') {
	 ...
 } 


If we consider 3 constructions: nested if , switch with the appropriate values ​​and checking the values ​​in the hash, then the following interesting feature should be noted. With a small if nesting level (if there are only a few values, or we very often exit the first or second value), the if and switch constructions overtake the hash by about 10%. If we have a lot of values, and they are all approximately equiprobable, then the hash works out in the general case already by 20% faster. This applies equally to setting variables, as well as calling functions. T, e. To create a branch with a function call, it is best to use a hash.

Returning to YASS . When analyzing a CSS selector, several subtasks can be distinguished, described as “the problem of choice”:



Summary table



Summarizing a small result for various ways of checking the string, you can create the following table:

TaskSolution tool
Numeric value checkNormal comparison ( == )
Check multiple numeric valuesComparing their amount
Check that the number is not zero, or check for existenceChecking the negation of a given variable ( ! )
Parsing a string and selecting parts into an arrayString.match(RegExp) or RegExp.exec(String)
Regular expression matching stringRegExp.test(String)
Check string for the presence of a substringString.indexOf(String)
Check string for exact match (or match one of a set of values)if without a cast ( === )
Selection depending on the exact value (values ​​1–2)Conditional if construction
Selection depending on the exact value (values ​​3–8)switch
Selection depending on the exact value (values ​​greater than 8)Hash with keys corresponding to values


Perhaps this table can be supplemented with some more cases, or refer to the article on the performance of simple constructions in JavaScript and draw the appropriate conclusions.

To be continued...

Source: https://habr.com/ru/post/50210/


All Articles