From WikiChip
Difference between revisions of "mirc/optimization"
< mirc

(Reinstate and change Ouims edit.)
(Add additional benchmark commands.)
Line 11: Line 11:
  
 
=== Use {{mIRC|Token Manipulation}} to break strings into pieces ===
 
=== Use {{mIRC|Token Manipulation}} to break strings into pieces ===
If you want to get the first part of a string up to (say) the first full-stop, don't use {{mirc|$pos}} to find the position of this character, and then use {{mirc|$left}} or {{mirc|$right}} to get the relevant sub-string. Instead use mIRCs {{mIRC|Token Manipulation}} functionality: $gettok(%string,1,$asc(.)) to get the first sentence (less the period itself) and $gettok(%string,2-,$asc(.)) to get the remainder of the paragraph.
+
If you want to get the first part of a string up to (say) the first full-stop, don't use {{mirc|$pos}} to find the position of this character, and then use {{mirc|$left}} or {{mirc|$right}} to get the relevant sub-string. Instead use mIRCs {{mIRC|Token Manipulation}} functionality: $gettok(%string,1,$asc(.)) to get the first sentence and $gettok(%string,2-,$asc(.)) to get the remainder of the paragraph.
 +
 
 +
<source lang="mIRC">/benchmark -ai100000 $gettok(Sentence. Sentence.,1,$asc(.));$left(Sentence. Sentence.,$pos(Sentence. Sentence.,.))</source>
  
 
=== Use {{mirc|wildcard|Wildcard-Matching}}  ===
 
=== Use {{mirc|wildcard|Wildcard-Matching}}  ===
Line 23: Line 25:
 
<source lang="mIRC">if (* fat * isin %string) echo -a I am not fat!!</source>
 
<source lang="mIRC">if (* fat * isin %string) echo -a I am not fat!!</source>
  
Wildcard-matches can be used in {{mirc|/if}} statements, but also in {{mirc|$wildtok|token manipulation}}, {{mirc|$hfind|hash tables}}, {{mirc|$fline|custom windows}}, {{mIRC|on events|ON events}}, {{mirc|$var|variables}}, etc. etc. Even if you cannot narrow it down to a single item, using wildcard-matches to reduce substantially the number of iterations of a loop is also very beneficial.  
+
<source lang="mIRC">if (* fat * isin %string) echo -a I am not fat!!</source>
 +
 
 +
Wildcard-matches can be used in {{mirc|/if}} statements, but also in {{mirc|$wildtok|token manipulation}}, {{mirc|$hfind|hash tables}}, {{mirc|$fline|custom windows}}, {{mIRC|on events|ON events}}, {{mirc|$var|variables}}, etc. etc. Even if you cannot narrow it down to a single item, using wildcard-matches to reduce substantially the number of iterations of a loop is very beneficial.  
  
 
So for example, if we want to find a particular line in a custom window, rather than:  
 
So for example, if we want to find a particular line in a custom window, rather than:  
Line 59: Line 63:
 
When calling any form of command or identifier mIRC will attempt to find a scripted version prior to looking for a native equivalent. This functionality can be bypassed by prefixing commands with <code>!</code> and by inserting a <code>~</code> after the <code>$</code> of identifiers.
 
When calling any form of command or identifier mIRC will attempt to find a scripted version prior to looking for a native equivalent. This functionality can be bypassed by prefixing commands with <code>!</code> and by inserting a <code>~</code> after the <code>$</code> of identifiers.
  
This bypasses mIRC looking for a scripted <code>echo</code> alias:
+
For example, this bypasses mIRC looking for a scripted <code>echo</code> alias:
<syntaxhighlight lang="mirc">!echo -a example1
+
<syntaxhighlight lang="mirc">/!echo -a Message</syntaxhighlight>
.!echo -a example2</syntaxhighlight>
+
 
 +
<syntaxhighlight lang="mirc">/benchmark -aci100000 !set -l %a 2;set -l %a 1</syntaxhighlight>
  
This bypasses mIRC looking for a scripted <code>me</code> alias<sup>1</sup>:
+
Alternatively this bypasses mIRC looking for a scripted <code>me</code> alias<sup>1</sup>:
 
<syntaxhighlight lang="mirc">echo -a $~me</syntaxhighlight>
 
<syntaxhighlight lang="mirc">echo -a $~me</syntaxhighlight>
 +
 +
<syntaxhighlight lang="mirc">/benchmark -ai100000 $~left(abc,2);$left(abc,2)</syntaxhighlight>
  
 
<sup>1</sup>: Even though mIRC will use its own native identifiers over custom aliases of the same name, there is still some pre-evaluation that can be bypassed using the above method.
 
<sup>1</sup>: Even though mIRC will use its own native identifiers over custom aliases of the same name, there is still some pre-evaluation that can be bypassed using the above method.
Line 75: Line 82:
 
if (condition) { command }</syntaxhighlight>
 
if (condition) { command }</syntaxhighlight>
  
 +
<syntaxhighlight lang="mirc">/benchmark -aci100000 if (0 == 1) var %a 1;if 0 == 1 { var %a 1 };if (0 == 1) { var %a 1 }</syntaxhighlight>
  
 
=== {{mirc|/if}} vs. {{mirc|$iif}}() ===
 
=== {{mirc|/if}} vs. {{mirc|$iif}}() ===
Line 80: Line 88:
  
 
Best to worst:
 
Best to worst:
 +
<syntaxhighlight lang="mirc">if (condition) var %result = condition_true_value
 +
else var %result = condition_false_value</syntaxhighlight>
 
<syntaxhighlight lang="mirc">var %result = condition_false_value
 
<syntaxhighlight lang="mirc">var %result = condition_false_value
 
if (condition) var %result = condition_true_value</syntaxhighlight>
 
if (condition) var %result = condition_true_value</syntaxhighlight>
<syntaxhighlight lang="mirc">if (condition) var %result = condition_true_value
 
else var %result = condition_false_value</syntaxhighlight>
 
 
<syntaxhighlight lang="mirc">var %result = $iif(condition, condition_true_value, condition_false_value)</syntaxhighlight>
 
<syntaxhighlight lang="mirc">var %result = $iif(condition, condition_true_value, condition_false_value)</syntaxhighlight>
 +
 +
<syntaxhighlight lang="mirc">/benchmark -aci100000 if (condition) var %result = condition_true_value | else var %result = condition_false_value;var %result = condition_false_value | if (condition) var %result = condition_true_value;var %result = $iif(condition, condition_true_value, condition_false_value)</syntaxhighlight>
  
 
=== {{mirc|/tokenize}} & $n vs {{mirc|$gettok}}() ===
 
=== {{mirc|/tokenize}} & $n vs {{mirc|$gettok}}() ===
Line 94: Line 104:
 
<sup>1</sup>: even with just two references against the same input tokenizing is faster than using $gettok
 
<sup>1</sup>: even with just two references against the same input tokenizing is faster than using $gettok
  
=== [ ] vs $() vs $eval ===
+
=== []'s vs $() vs $eval ===
 
Best to worst:
 
Best to worst:
 
<syntaxhighlight lang="mirc">
 
<syntaxhighlight lang="mirc">
Line 103: Line 113:
 
'''Note:''' "$($+(%,var,%i))" is, however, far more readable than "[ % [ $+ [ var [ $+ [ %i ] ] ] ] ]".
 
'''Note:''' "$($+(%,var,%i))" is, however, far more readable than "[ % [ $+ [ var [ $+ [ %i ] ] ] ] ]".
  
=== /var vs. /set ===
+
=== /var vs /set ===
Best to worst:
 
 
 
'''Global'''
 
<syntaxhighlight lang="mirc">
 
%a = 1
 
/set %a 1
 
/set %a = 1
 
/var -g %a 1
 
/var -g %a = 1
 
</syntaxhighlight>
 
 
 
<syntaxhighlight lang="mirc">/benchmark -aci100000 %a = 1;/set %a 1;/set %a = 1;/var -g %a 1;/var -g %a = 1</syntaxhighlight>
 
 
 
'''Local'''
 
<syntaxhighlight lang="mirc">
 
/set -l %a 1
 
/set -l %a = 1
 
/var %a 1
 
/var %a = 1
 
</syntaxhighlight>
 
 
 
<syntaxhighlight lang="mirc">/benchmark -aci100000 /set -l %a 1;/set -l %a = 1;/var %a 1;/var %a = 1</syntaxhighlight>
 
 
 
== Benchmarking ==
 
 
 
Here is the script used to benchmark alternative forms of script code:
 
<syntaxhighlight lang="mirc">
 
alias benchmark {
 
  if ($show) {
 
    if ($0 == 0) {
 
      echo -cag action $str(-,80)
 
      echo 4 -ag /benchmark -aciN string1[;string2[;string3 …]]
 
      echo -ag $crlf
 
      echo -cag info The default number of iterations is 10000.
 
      echo -cag info -i switch defines a different number of iterations.
 
      echo -cag info -c tests as a command, not a string
 
      echo -cag info -a applies the same switches to all strings
 
      echo -ag $crlf
 
      echo -cag action Examples:
 
      echo -cag info /benchmark $ $+ replace(a,a,b)
 
      echo -cag info /benchmark -i5000 $ $+ replace(a,a,b)
 
      echo -cag info /benchmark -i5000 $ $+ replace(a,a,b);-i5000 $ $+ regsubex(a,a,b)
 
      echo -cag info /benchmark -ai5000 $ $+ replace(a,a,b);$ $+ regsubex(a,a,b)
 
      echo -ag $crlf
 
      echo -cag info /benchmark -c var % $+ a 1;-c set -l % $+ a 1
 
      echo -cag info /benchmark -ci20000 var % $+ a 1;-ci20000 set -l % $+ a 1
 
      echo -cag info /benchmark -aci20000 var % $+ a 1;set -l % $+ a 1
 
      echo -cag action $str(-,80)
 
      return
 
    }
 
    echo -cag action Benchmark $1-
 
    if ($switch($1,a)) {
 
      var %o $iif($remove($1,a) != -,$v1)
 
      tokenize 32 $2-
 
    }
 
    tokenize 59 $1-
 
    var %a 1
 
    while ($(,%o $ $+ %a) != %o) {
 
      scon 0 .benchmark $!v1
 
      inc %a
 
    }
 
    return
 
  }
 
  var %benchmark $iif($switch($1,i) isnum,$v1,10000)
 
  var %b %benchmark,%s $1
 
  if ($switch(%s)) tokenize 32 $2-
 
  tokenize 32 $iif(!$switch(%s,c),noop) $1-
 
  var %t $ticks
 
  while (%benchmark) {
 
    scon 0 $1-
 
    dec %benchmark
 
  }
 
  var %e $ticks - %t
 
  var %c = $switch(%s,c)
 
  echo 3 -ag %b iterations of the $iif(%c,command,string) $qt($iif(%c,$1-,$2-)) took %e $+ ms
 
  return
 
  :error
 
  echo -cag info $error
 
  echo 4 -ag Could not complete benchmark test for $qt($iif($1 != noop,$1) $2-)
 
  reseterror
 
}
 
 
 
alias -l switch {
 
  if (-?* !iswm $1) return $false
 
  if (!$regex($1,/([a-zA-Z](?:".+?"|[^a-zA-Z]+)?)/g)) return $false
 
  if ($2 isnum && $2 > $regml(0)) return $false
 
  if ($2 = $null) return $true
 
  if ($2 isnum) {
 
    var %m $regml($2),%s $left(%m,1),%p $noqt($mid(%m,2))
 
    if ($2 = 0) return $regml(0)
 
    if (%m != $null) {
 
      if ($regex($prop,/^exists?$/i)) return $true
 
      if ($regex($prop,/^contents?$/i)) return $iif(%p,%p,$false)
 
      return %s
 
    }
 
    return $false
 
  }
 
  var %x 1
 
  while ($regml(%x)) {
 
    var %m $v1,%s $left($v1,1),%p $noqt($mid($v1,2))
 
    if ($2 === %s) {
 
      if ($prop != sname) return $iif(%p,%p,$true)
 
      elseif ($prop = sname) return %s
 
    }
 
    inc %x
 
  }
 
  return $false
 
}</syntaxhighlight>
 
  
Copy and paste the above script into a new script file called "benchmark.mrc" and type /benchmark to get instructions.
+
multiple /set -l to set local variables is much faster than /var because /var has to parse the line and call /set for each variable anyway.
  
 
[[Category:mIRC|optimization]]
 
[[Category:mIRC|optimization]]

Revision as of 13:12, 13 September 2018


mIRC Script Language is an interpreted language - which means that mIRC has to work out what each statement means each and every time it is executed. Consequently it is not considered a fast language and, more often than not, the easiest implementation is not the fastest.

That said, PCs today are far more powerful then when mIRC first introduced its scripting language, and unless your script is processing large number of messages or large files, then performance is less of an issue than it used to be. On the other hand, the maintainability of your script is also important, so eliminating duplicate code using common aliases & identifiers is also beneficial even if it introduces some minor overheads.

The following tips will help to increase the execution speed of a script. Most will have a very marginal speed advantage and may not be worth consideration outside of long-running script blocks such as loops.

Use built-in functionality

Because mIRC interprets each statement every time it executes it, reducing the number of statements executed is the easiest way to improve the performance of your scripts. By contrast, once mIRC has worked out what the statement is, then the execution is done by mIRCs compiled code. You can often replace several statements or even entire loops by clever use of mIRCs broad range of built-in functionality. Here are some examples of common ways to reduce the number of statements:

Use Token Manipulation to break strings into pieces

If you want to get the first part of a string up to (say) the first full-stop, don't use $pos to find the position of this character, and then use $left or $right to get the relevant sub-string. Instead use mIRCs Token Manipulation functionality: $gettok(%string,1,$asc(.)) to get the first sentence and $gettok(%string,2-,$asc(.)) to get the remainder of the paragraph.

/benchmark -ai100000 $gettok(Sentence. Sentence.,1,$asc(.));$left(Sentence. Sentence.,$pos(Sentence. Sentence.,.))

Use Wildcard-Matching

Wildcard-matching allows you to see if your string matches a wildcard template containing fixed text and wildcard-match characters ?, * and &.

"?" match any character
"*" match 0 or more characters
" & " match any word

So if you want to see if a string contains the word "fat" then you can use:

if (* fat * isin %string) echo -a I am not fat!!
if (* fat * isin %string) echo -a I am not fat!!

Wildcard-matches can be used in /if statements, but also in token manipulation, hash tables, custom windows, ON events, variables, etc. etc. Even if you cannot narrow it down to a single item, using wildcard-matches to reduce substantially the number of iterations of a loop is very beneficial.

So for example, if we want to find a particular line in a custom window, rather than:

var %i $line(@custwin,0)
while (%i) {
  var %l = $line(@custwin,%i)
  if (The cat sat on the * iswm %l) echo -a I have found what the cat sat on. %l
  dec %i
}

the following code loops far fewer times and is therefore much quicker:

var %match The cat sat on the *, %i $fline(@custwin,%match,0)
while (%i) {
  var %l = $fline(@custwin,%match,%i)
  echo -a I have found what the cat sat on. %l
  dec %i
}

Use regular expressions

Regular expressions are a very powerful text search and replacement tool even if they have a significant learning curve.

Again, regular expressions can be used in hash tables, ON events, etc. etc.

Use hash tables

If you have a list of wildmatches you want to search for, store them in a hash table and use $hfind so that with one statement mIRC loops through the wildmatches to find one which is a match for your string.

Optimise to help mIRC be faster

Some mIRC Scripting Language constructs are faster than others...

Organization

Aliases at the top of script files have faster access speeds than if placed at the bottom of the script. Furthermore, aliases in scripts at the top of the script-order have faster access speeds than those at the end.

Alias Bypassing

When calling any form of command or identifier mIRC will attempt to find a scripted version prior to looking for a native equivalent. This functionality can be bypassed by prefixing commands with ! and by inserting a ~ after the $ of identifiers.

For example, this bypasses mIRC looking for a scripted echo alias:

/!echo -a Message
/benchmark -aci100000 !set -l %a 2;set -l %a 1

Alternatively this bypasses mIRC looking for a scripted me alias1:

echo -a $~me
/benchmark -ai100000 $~left(abc,2);$left(abc,2)

1: Even though mIRC will use its own native identifiers over custom aliases of the same name, there is still some pre-evaluation that can be bypassed using the above method.

Conditions

Best to worst:

if (condition) command
if condition { command }
if (condition) { command }
/benchmark -aci100000 if (0 == 1) var %a 1;if 0 == 1 { var %a 1 };if (0 == 1) { var %a 1 }

/if vs. $iif()

$iif() is much slower than using an if-else statement. When $iif() is encounter it is first rearranged into an if-else statement and the result is evaluated.

Best to worst:

if (condition) var %result = condition_true_value
else var %result = condition_false_value
var %result = condition_false_value
if (condition) var %result = condition_true_value
var %result = $iif(condition, condition_true_value, condition_false_value)
/benchmark -aci100000 if (condition) var %result = condition_true_value | else var %result = condition_false_value;var %result = condition_false_value | if (condition) var %result = condition_true_value;var %result = $iif(condition, condition_true_value, condition_false_value)

/tokenize & $n vs $gettok()

For successive1 calls against the same data, it is faster to use /tokenize and $n over $gettok().

;; faster than using $gettok(a b c, 1, 32) $gettok(a b c, 2, 32)
tokenize 32 a b c
echo -a $1 $2

1: even with just two references against the same input tokenizing is faster than using $gettok

[]'s vs $() vs $eval

Best to worst:

[ eval_statement ]
$(eval_statement, 2)
$eval(eval_statement, 2)

Note: "$($+(%,var,%i))" is, however, far more readable than "[ % [ $+ [ var [ $+ [ %i ] ] ] ] ]".

/var vs /set

multiple /set -l to set local variables is much faster than /var because /var has to parse the line and call /set for each variable anyway.