Difference between revisions of "String Parsing Optimization"

From Flexible Survival
Jump to: navigation, search
([one of][or][at random])
 
(12 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<br> String Parsing is a powerful language for designing content on the Flexible Survival MUCK. The language permits read functions, write functions, and allows users to create dynamic content based on a combination of those functions. Its semantics were originally designed by Damaged and later rewritten by Fauna for increased performance.
+
<br> String Parsing is a scripting language for designing content on the Flexible Survival MUCK. The language supports read functions, write functions, and allows users to create dynamic content based on a combination of those functions. The language was originally designed and coded by Damaged and later refactored by Fauna for improved performance.
  
String Parsing, like other markup languages, does cause a hit in performance when inserted into plain text. This article exists to provide solutions for those seeking to squeeze the most performance out of their code. It will not tell you how to write clean or legible code, NOR will it teach you the fundamental markup of String Parsing. For information on those subjects, see the wiki article on [http://wiki.flexiblesurvival.com/w/String_Parsing String Parsing] and review the in-game manual.
+
String Parsing by nature reduces performance when inserted into plain text. This article exists as a reference for those seeking to squeeze the most performance out of their code without reducing readability. For information on how to write parsing, see the wiki article on [http://wiki.flexiblesurvival.com/w/String_Parsing String Parsing] and review the in-game manual.
  
 
<br>
 
<br>
Line 7: Line 7:
 
== Performance: Why should I care? ==
 
== Performance: Why should I care? ==
  
Performance is important for a number of reasons.
+
1) Optimized code reduces the perceived latency of navigation in a MUCK.
:Optimized code reduces the perceived latency of navigation in a MUCK. Doing so should be enjoyable on even the most sluggish of connections.
+
 
:Optimized code reduces the Flexible Survival server load, thus permitting us to provide more and more detailed user-visible content.
+
2) Optimized code reduces the Flexible Survival server load.
:Optimized code reduces the memory cost of programs and #dbrefs. In the case of a fatal memory spike or other unforeseen circumstances, the chances of services ceasing function are reduced when baseline memory is kept to a reasonable minimum.
+
 
 +
3) Optimized code reduces the memory cost of programs and database references (#dbrefs).
  
 
=== How does this relate to String Parsing? ===
 
=== How does this relate to String Parsing? ===
  
String Parsing is a markup language found throughout many aspects of the game. From transformation messages, to sex scenes, to exploration, to NPC interaction, and more, String Parsing is the gold standard for ensuring high quality content with minimal perceived latency and maximum ease of deployment. Despite this, we must keep an eye on our code to ensure continued high quality submissions and end-user satisfaction.
+
String Parsing is pervasive. It can be placed in transformation messages, sex scenes, rooms, NPCs, and more. As a result, it's important to ensure that this code is functional, readable, and swift to execute.
 
 
Strictly speaking, a more important language to keep optimized is the MUF backend that Flexible Survival is based upon. However, since this guide is targeted at a general audience and not Mucker-level or higher staff, String Parsing tips and tricks will be the subject matter I'll focus on.
 
  
For the subsequent sections, please use the in-game @viewparse command to indent and colorize markup for heightened legibility.
+
For the subsequent sections, please use the in-game @viewparse command to indent and colorize markup for better legibility.
  
 
== Conditionals ==
 
== Conditionals ==
 
Conditionals are the heart, brain, and skeletal structure of the String Parsing body.
 
  
 
=== [if ][end if] ===
 
=== [if ][end if] ===
 
----
 
----
A basic [if ] statement is prone to some syntactic errors, but little in terms of optimization woes.
+
A basic [if ] statement is prone to syntactic errors.
  
 +
For genitals, appearance, and time, always use the template [if [target] is <thing>]. The "is" function has been optimized for these checks.
  
Always use the template [if [target] is <gender>] and [if time is <time of day/season>]. Do not use [if [target] = <gender>] or [if time = <time of day/season>]. Despite taking less space in the grand scheme of things, the 'is' function is specifically optimized for checking a target's gender and the time of day/season.
+
::'''<span style="color:#FF0000">[if [player] = neuter]
::'''<span style="color:#FF0000">[if [target] = <gender>]
+
::'''<span style="color:#FF0000">[if [player] = neutral]
::'''<span style="color:#FF0000">[if time = <time of day/season>]
+
::'''<span style="color:#FF0000">[if time = day]
::'''<span style="color:#008000">[if [target] is <gender>]
+
::'''<span style="color:#008000">[if [player] is neuter]
::'''<span style="color:#008000">[if time is <time of day/season>]
+
::'''<span style="color:#008000">[if [player] is neutral]
 +
::'''<span style="color:#008000">[if time is day]
  
Do not check for integers with the '=' sign. This is a function designed to check a string. Use '==' instead.
+
Use "==" to evaluate integers. Benchmarks have shown that this executes faster than checking for a string with "=".
Benchmarks have shown that checking directly for an integer is both faster and cleaner.
+
 
::'''<span style="color:#FF0000">[if stat cocks of [target] = 1]
+
::'''<span style="color:#FF0000">[if stat cocks of [player] = 1]
::'''<span style="color:#008000">[if stat cocks of [target] == 1]
+
::'''<span style="color:#008000">[if stat cocks of [player] == 1]
  
 
Avoid not statements when possible. For example, [if time is not night] is the same as [if time is day].
 
Avoid not statements when possible. For example, [if time is not night] is the same as [if time is day].
 +
 
::'''<span style="color:#FF0000">[if time is not night]
 
::'''<span style="color:#FF0000">[if time is not night]
 
::'''<span style="color:#FF0000">[if time is not day]
 
::'''<span style="color:#FF0000">[if time is not day]
Line 49: Line 49:
 
=== [if ][else][end if] ===
 
=== [if ][else][end if] ===
 
----
 
----
[else] creates potential for redundancy if a coder is not paying attention. Here is a small before-and-after list of sentences, followed by clear instruction.
+
Try to avoid duplicated words when using the [if ][else] structure.
 
 
 
 
::'''<span style="color:#FF0000">[if time is winter]The large bear returned to his cave to hibernate.[else]The small fox jumped over the fence.[end if]
 
 
 
::'''<span style="color:#008000">The [if time is winter]large bear returned to his cave to hibernat[else]small fox jumped over the fenc[end if]e.
 
 
 
 
 
In the above example, repeated text has been removed from the [if ] statement itself and added as prefix and suffix to the code, respectively. This provides the exact same user-visible output with less characters and processing requirements.
 
  
  
Line 65: Line 57:
  
  
See above. Repeated text is removed from the [if ] statement itself and added as prefix and suffix to the code, respectively.
+
In the above example, repeated text has been removed from the [if ][else] structure and added as a prefix. This provides the exact same user-visible output with less characters. Processing requirements are identical, but the text fits in less space and is just as readable.
 +
 
 +
Note: Do not do this for letters that are part of a word. Splitting words in text results in less readable code and hampers assistive technology like screen readers.
 
<br>
 
<br>
  
 
=== [if ][else if ][else][end if] ===
 
=== [if ][else if ][else][end if] ===
 
----
 
----
[else if ] introduces further potential for code slowdown. Please read the following steps carefully to avoid that.
+
This structure checks statements in brackets one after another, left to right. If a statement evaluates as true, the code stops and prints the text within. The structure requires consideration for order of events.
 
 
 
 
::'''<span style="color:#FF0000">You [if [player] is male]try not to grow aroused by the Skunk Girl's sumptuous body, whining in protest.[else if [player] is female]easily ignore the Skunk Girl's attempts to seduce you.[else]easily ignore the Skunk Girl's attempts to seduce you. Neuters 4ever![end if]
 
  
::'''<span style="color:#008000">You [if [player] is male]try not to grow aroused by the Skunk Girl's sumptuous body, whining in protest[else]easily ignore the Skunk Girl's attempts to seduce you[end if].[if [player] is neuter] Neuters 4ever![end if]
 
  
 +
::'''<span style="color:#FF0000">[if [player] is pure male]You are male[else if [player] is pure female]You are female[else if [player] is herm]You are a herm[else]You are neuter[end if].
  
In the above example, a lengthy [if ][else if ][else][end if] statement is deconstructed into [if ][else][end if][if ][end if].
+
::'''<span style="color:#008000">[if [player] is herm]You are a herm[else if [player] is female]You are female[else if [player] is male]You are male[else]You are neuter[end if].
  
Converge the [else if ] and [else] statements into a single [else]. Move the neuter check into a separate [if ] statement at the end. Move the trailing periods in the [if ][else][end if] statement before the start of the neuter check.
 
  
 +
The full logic of the first snippet is
  
::'''<span style="color:#FF0000">The [if time is day]sun is [end if][if time is morning]rising, stirring all forms of life into motion[else if time is afternoon]high, baking the earth with its furious light[else if time is evening]starting to set, casting the land in long shadows[else]moon shines over the land[end if].
+
:[if (stat cocks of [player] > 0) and (stat cunts of [player] == 0)][else if (stat cunts of [player] > 0) and (stat cocks of [player] == 0)][else if (stat cocks of [player] > 0) and (stat cunts of [player] > 0)][else][end if]
  
::'''<span style="color:#FF0000">The [if time is morning]sun is rising, stirring all forms of life into motion[else if time is afternoon]sun is high, baking the earth with its furious light[else if time is evening]sun is starting to set, casting the land in long shadows[else]moon shines over the land[end if].
+
The second snippet simplifies this logic to the equivalent of
  
::'''<span style="color:#008000">The [if time is night]moon shines over the land[else if time is morning]sun is rising, stirring all forms of life into motion[else if time is evening]sun is starting to set, casting the land in long shadows[else]sun is high, baking the earth with its furious light[end if].
+
:[if (stat cocks of [player] > 0) and (stat cunts of [player] > 0)][else if stat cunts of [player] > 0][else if stat cocks of [player] > 0][else][end if]
 
 
 
 
In the above example, a couple optimizations are made.
 
 
 
Place [if time is night] at the front. This is the longest period of time and ensuring it triggers first saves execution costs throughout most of the 24-hour cycle. Use the check for 'afternoon' as the final [else], because even though it is just as long a period as morning and evening, the name has more characters in it.
 
  
 
=== [case ] ===
 
=== [case ] ===
Line 99: Line 85:
  
  
::'''<span style="color:#FF0000">[case stat X of [target] ==][when (target) 1]A[end when][when (target) 2]B[end when][when (target) 3]C[end when][when (target) 4]D[end when][when 1]E[end when]
+
::'''<span style="color:#FF0000">[case stat X of [player] ==][when (target) 1]A[end when][when (target) 2]B[end when][when (target) 3]C[end when][when (target) 4]D[end when][when 1]E[end when]
  
::'''<span style="color:#008000">[case [stat X of [target]] ==][when (target) 1]A[end when][when (target) 2]B[end when][when (target) 3]C[end when][when (target) 4]D[end when][when 1]E[end when]
+
::'''<span style="color:#008000">[case [stat X of [player]] ==][when (target) 1]A[end when][when (target) 2]B[end when][when (target) 3]C[end when][when (target) 4]D[end when][when 1]E[end when]
  
  
If something is valid parsing on its own, such as '[stat X of [target]]' and '[the mutation X of [target]]', use that, so the statement will only have to be parsed once instead of in every [when ]-statement.
+
If something is valid parsing on its own, such as '[stat X of [player]]' and '[the mutation X of [player]]', use that, so the stat will only have to be read once instead of in every [when ]-statement.
  
  
::'''<span style="color:#FF0000">[if stat blah/bluh/bleh of [target] == 1]A[else if stat blah/bluh/bleh of [target] == 2]B[else if stat blah/bluh/bleh of [target] == 3]C[else if stat blah/bluh/bleh of [target] == 4]D[else if stat blah/bluh/bleh of [target] == 5]E[else]F[end if]
+
::'''<span style="color:#FF0000">[if stat placeholder of [player] == 1]A[else if stat placeholder of [player] == 2]B[else if stat placeholder of [player] == 3]C[else if stat placeholder of [player] == 4]D[else if stat placeholder of [player] == 5]E[else]F[end if]
  
::'''<span style="color:#008000">[case [stat blah/bluh/bleh of [target]] ==][when (target) 1]A[end when][when (target) 2]B[end when][when (target) 3]C[end when][when (target) 4]D[end when][when (target) 5]E[end when][when 1]F[end when][end case]
+
::'''<span style="color:#008000">[case [stat placeholder of [player]] ==][when (target) 1]A[end when][when (target) 2]B[end when][when (target) 3]C[end when][when (target) 4]D[end when][when (target) 5]E[end when][when 1]F[end when][end case]
  
  
In the above example, a complex [if ][else if ][end if] statement is optimized.
+
In the above example, a complex [if ]-chain is optimized to only read a stat once and evaluate its stored value.
 
 
Convert the code to a basic case-statement. Since all comparisons are alike, remove them from their [when ] statements and append one to [case (target)]. Add brackets around 'stat blah/bluh of [player]' to avoid computing that in every [when ] statement.
 
  
 
== Randomization ==
 
== Randomization ==
Line 125: Line 109:
  
  
::'''<span style="color:#FF0000">Sometimes, a man's gotta man. [one of]Other times,[or]Sometimes,[at random] he doesn't.
+
::'''<span style="color:#FF0000">Sometimes, you need to do something. [one of]Other times,[or]Sometimes,[at random] you don't.
  
::'''<span style="color:#008000">Sometimes, a man's gotta man. [one of]Other [or]Some[at random]times, he doesn't.
+
::'''<span style="color:#008000">Sometimes, you need to do something. [one of]Other times[or]Sometimes[at random], you don't.
  
  
In the above example, redundancy is nixed by moving a string shared by all inputs after [at random].
+
In the above example, text redundancy is reduced by moving a comma after [at random].
  
  
Line 136: Line 120:
  
 
[with odds: ] allows you to simplify many longer [one of] statements.
 
[with odds: ] allows you to simplify many longer [one of] statements.
 +
 +
Example 1:
  
  
Line 142: Line 128:
 
::'''<span style="color:#008000">[one of]1[or]2[with odds: 5 1]
 
::'''<span style="color:#008000">[one of]1[or]2[with odds: 5 1]
  
or
+
 
 +
Example 2:
  
 
::'''<span style="color:#FF0000">[one of]1[or]1[or]1[or]2[or]2[or]3[at random]
 
::'''<span style="color:#FF0000">[one of]1[or]1[or]1[or]2[or]2[or]3[at random]
Line 150: Line 137:
 
=== [random x to y] ===
 
=== [random x to y] ===
 
----
 
----
This function is more specialized than [one of][or][at random], lending itself well to optimization. Note that it only accepts numerical input (negatives included) and that 'y' should be greater than 'x'.
+
This function is specialized for ranges of values. Note that it only accepts integers (negative ones included) and that 'x' should be less than 'y'.
  
  
Line 156: Line 143:
  
 
::'''<span style="color:#008000">[random 1 to 6]
 
::'''<span style="color:#008000">[random 1 to 6]
 
 
A complex [one of][or][at random] statement converted into a [random x to y] statement.
 
 
 
::'''<span style="color:#FF0000">[one of]1[or]1[or]1[or]1[or]1[or]1[or]1[or]1[or]1[or]2[at random]
 
 
::'''<span style="color:#008000">[if [random 1 to 10] < 10]1[else]2[end if]
 
 
 
The above example illustrates how to make one value fire more frequently than the other.
 
 
 
::'''<span style="color:#FF0000">[case [random 1 to 15] <][when (target) 6]1[end when][when (target) 10]2[end when][when (target) 13]3[end when][when (target) 15]4[end when][when 1]5[end when][end case]
 
 
::'''<span style="color:#008000">[one of]1[or]1[or]1[or]1[or]1[or]2[or]2[or]2[or]2[or]3[or]3[or]3[or]4[or]4[or]5[at random]
 
 
 
'''Do NOT''' try to be too fancy with [random x to y]. The above example illustrates how a case-statement can result in longer code.
 
 
'''Do NOT''' use [if ][else if ][else][end if]. Each roll would be independent and result in skewed output probability.
 
 
 
::'''<span style="color:#FF0000">[one of]Hello sir[or]Hello sir[or]Hello sir[or]Hello sir[or]Hello sir[or]Hi there[or]Hi there[or]Hi there[or]Hi there[or]Ola Mister Cunningham[or]Ola Mister Cunningham[or]Ola Mister Cunningham[or]Heya Cunnin'[or]Heya Cunnin'[or]Good day Mr. Cunningham[at random]
 
 
::'''<span style="color:#008000">[case [random 1 to 15] <][when (target) 6]Hello sir[end when][when (target) 10]Hi there[end when][when (target) 13]Ola Mister Cunningham[end when][when (target) 15]Heya Cunnin'[end when][when 1]Good day Mr. Cunningham[end when][end case]
 
 
 
The above example illustrates an overall reduction of characters using [case ] with longer input. A general rule of thumb is that the longer the (target) and input values are, the more likely [case ] is to reduce overall characters needed.
 
  
 
<br>
 
<br>
 
----
 
----
 
<br>
 
<br>
 
 
 
 
This document is a work in progress and is thus subject to change. All views and opinions expressed in here are solely Songbird's. Please only modify with either A), explicit permission to do so, or B), a fix for known factual inaccuracy / grammar errors.
 
  
 
[[Category:Guides]][[Category:Code]]
 
[[Category:Guides]][[Category:Code]]

Latest revision as of 20:17, 26 August 2020


String Parsing is a scripting language for designing content on the Flexible Survival MUCK. The language supports read functions, write functions, and allows users to create dynamic content based on a combination of those functions. The language was originally designed and coded by Damaged and later refactored by Fauna for improved performance.

String Parsing by nature reduces performance when inserted into plain text. This article exists as a reference for those seeking to squeeze the most performance out of their code without reducing readability. For information on how to write parsing, see the wiki article on String Parsing and review the in-game manual.


Performance: Why should I care?

1) Optimized code reduces the perceived latency of navigation in a MUCK.

2) Optimized code reduces the Flexible Survival server load.

3) Optimized code reduces the memory cost of programs and database references (#dbrefs).

How does this relate to String Parsing?

String Parsing is pervasive. It can be placed in transformation messages, sex scenes, rooms, NPCs, and more. As a result, it's important to ensure that this code is functional, readable, and swift to execute.

For the subsequent sections, please use the in-game @viewparse command to indent and colorize markup for better legibility.

Conditionals

[if ][end if]


A basic [if ] statement is prone to syntactic errors.

For genitals, appearance, and time, always use the template [if [target] is <thing>]. The "is" function has been optimized for these checks.

[if [player] = neuter]
[if [player] = neutral]
[if time = day]
[if [player] is neuter]
[if [player] is neutral]
[if time is day]

Use "==" to evaluate integers. Benchmarks have shown that this executes faster than checking for a string with "=".

[if stat cocks of [player] = 1]
[if stat cocks of [player] == 1]

Avoid not statements when possible. For example, [if time is not night] is the same as [if time is day].

[if time is not night]
[if time is not day]
[if time is day]
[if time is night]


[if ][else][end if]


Try to avoid duplicated words when using the [if ][else] structure.


[if time is day]A woman in tight jeans and an unassuming blouse mans the counter. She's twirling a pen in her fingers, seemingly disinterested in her body of work.[else]A woman in tight jeans and an unassuming blouse would normally man the counter. However, she doesn't appear to be here right now.[end if]
A woman in tight jeans and an unassuming blouse [if time is day]mans the counter. She's twirling a pen in her fingers, seemingly disinterested in her body of work[else]would normally man the counter. However, she doesn't appear to be here right now[end if].


In the above example, repeated text has been removed from the [if ][else] structure and added as a prefix. This provides the exact same user-visible output with less characters. Processing requirements are identical, but the text fits in less space and is just as readable.

Note: Do not do this for letters that are part of a word. Splitting words in text results in less readable code and hampers assistive technology like screen readers.

[if ][else if ][else][end if]


This structure checks statements in brackets one after another, left to right. If a statement evaluates as true, the code stops and prints the text within. The structure requires consideration for order of events.


[if [player] is pure male]You are male[else if [player] is pure female]You are female[else if [player] is herm]You are a herm[else]You are neuter[end if].
[if [player] is herm]You are a herm[else if [player] is female]You are female[else if [player] is male]You are male[else]You are neuter[end if].


The full logic of the first snippet is

[if (stat cocks of [player] > 0) and (stat cunts of [player] == 0)][else if (stat cunts of [player] > 0) and (stat cocks of [player] == 0)][else if (stat cocks of [player] > 0) and (stat cunts of [player] > 0)][else][end if]

The second snippet simplifies this logic to the equivalent of

[if (stat cocks of [player] > 0) and (stat cunts of [player] > 0)][else if stat cunts of [player] > 0][else if stat cocks of [player] > 0][else][end if]

[case ]


Case-statements are useful where lengthy [if ][else if ][end if] statements would result in redundancy.


[case stat X of [player] ==][when (target) 1]A[end when][when (target) 2]B[end when][when (target) 3]C[end when][when (target) 4]D[end when][when 1]E[end when]
[case [stat X of [player]] ==][when (target) 1]A[end when][when (target) 2]B[end when][when (target) 3]C[end when][when (target) 4]D[end when][when 1]E[end when]


If something is valid parsing on its own, such as '[stat X of [player]]' and '[the mutation X of [player]]', use that, so the stat will only have to be read once instead of in every [when ]-statement.


[if stat placeholder of [player] == 1]A[else if stat placeholder of [player] == 2]B[else if stat placeholder of [player] == 3]C[else if stat placeholder of [player] == 4]D[else if stat placeholder of [player] == 5]E[else]F[end if]
[case [stat placeholder of [player]] ==][when (target) 1]A[end when][when (target) 2]B[end when][when (target) 3]C[end when][when (target) 4]D[end when][when (target) 5]E[end when][when 1]F[end when][end case]


In the above example, a complex [if ]-chain is optimized to only read a stat once and evaluate its stored value.

Randomization

The following functions display content at random. Input is separated by [or].

[one of][or][at random]


The standard [one of][or][at random] statement is prone to redundancy.


Sometimes, you need to do something. [one of]Other times,[or]Sometimes,[at random] you don't.
Sometimes, you need to do something. [one of]Other times[or]Sometimes[at random], you don't.


In the above example, text redundancy is reduced by moving a comma after [at random].


[with odds: ]

[with odds: ] allows you to simplify many longer [one of] statements.

Example 1:


[one of]1[or]1[or]1[or]1[or]1[or]2[at random]
[one of]1[or]2[with odds: 5 1]


Example 2:

[one of]1[or]1[or]1[or]2[or]2[or]3[at random]
[one of]1[or]2[or]3[with odds: 3 2 1]

[random x to y]


This function is specialized for ranges of values. Note that it only accepts integers (negative ones included) and that 'x' should be less than 'y'.


[one of]1[or]2[or]3[or]4[or]5[or]6[at random]
[random 1 to 6]