Large Game Speedup by Nathanael Nerode


Chapter - The Problem

Inform's Standard Rules take some shortcuts which are acceptable for most games, but which become inefficient in very large games. In particular, the "look" action can take several times longer than necessary.

If your game has less than a hundred things, you probably don't need this extension. (It won't hurt, though.) The problems we're talking about arise when the game has four or five hundred things defined.

(Of course, if you have hundreds of things *in one room*, that will always be slow! And this extension does not address the problem of having big slow "every turn" rules. This is purely about the "look" action, and some related phrases that print lists.)

The problem, in brief, is code such as

now all things are not mentioned;

or

repeat through all things:

(Or the Inform 6 equivalents.) The Standard Rules have such code in a few places. This extension removes some (though not all) of these.

You may need this extension even with 50 things if your code is hitting the loops repeatedly. For instance, my (Nathanael Nerode's) initial implementation of a clothing system ran a loop over all objects *inside* another loop over all objects, and did so 4 times every turn. This extension reduces this double loop to looping over the (generally short) list of relevant objects.

Chapter - The Solutions

Section - Empty

We define an efficient "empty" adjective for containers and supporters. The Standard Rules do not define this, and it's tempting to use idioms such as "if nothing is in the box" or "if the number of things in the box is zero". These are slow. The "if the box is empty" term defined here is fast.

(If you have defined an "empty" term for your containers or supporters, this extension will conflict with it. Sorry.)

Section - Locale Description

We improve the code that handles the Table of Locale Priorities. (Which is defined as having a blank row for each thing in the game.) It no longer has to repeat through or sort the entire table; it just deals with as many rows as needed to manage the current room.

Section - ListWriter

We improve the I6 list writer. Again, this removes most of the places where the code was iterating through all objects.

We define more efficient "to say the list of..." phrases. One often writes phrases like

say the list of things in the fridge;
say a list of people on the sofa;

Despite their appearance, these wind up iterating through the entire universe, not just the container or supporter in question. This extension defines fast alternatives:

say the list of things *in the fridge;
say a list of people *in the sofa;

The star indicates that the "*in" phrase is mandatory, and understood as a parent object to iterate through. (Note that it is "*in", not "*on", even for supporters.)

Section - Parts

You'd think that a phrase like

list of things which are part of the stove

would be quick... but again, it runs through every object in the entire game universe.

As a more efficient substitute, we provide:

the/-- list of components of (main thing - an object)

Also, for some reason, Inform 7 doesn't provide iterators for the component "parts of" tree hierarchy. This fills that gap. We define:

the/-- first component of (main thing - an object)
the/-- next component after (other component - an object)

These are used in exactly the same way as the existing Inform 7 phrases:

the/-- first thing held by (holder - an object)
the/-- next thing held after (held item - an object)

Section - Static Object Grouping

Static object grouping is UNTESTED in Inform 6M62 and may not work.

Finally, we define an alternate way to group objects in lists. The Standard Rules recompute object grouping every time a list is printed. But in most games, object groups are fixed -- perhaps the Tarot cards are one group, the Scrabble tiles are another group, and so on. So it's possible to compute this once, when the game begins, and then leave it alone.

Because this alternate plan is less flexible, it doesn't happen automatically. You have to invoke it by defining an option:

Use static object grouping.

You must then modify all your "Before listing contents..." grouping rules. If you have a rule such as

Before listing contents:
     group Tarot cards together.

... change it to:

Rule for initially listing contents:
     initially group Tarot cards together.

Do *not* use the standard "group X together" phrases when static option grouping is on; always use the "initially group X together" form. (The "...giving articles" and "...as (text)" variants are available.) And don't do either in a "before listing contents" rule. Move all of this logic to a "rule for initially listing contents".

Chapter - Changelog

Version 5/210908 added some missing rule response labels to the optimized you-can-also-see rule, and changed a "here" to "[here]" -- ZL Version 5/210325 added more section subdivision and reommitted some unnecessary code when Room Description Control is active. Version 5/210324 reverted the changes from 5/210322 as they caused unexpected errors. Version 5/210323 adopted the Inform 6M62-safe code for the *in phrases from the version in Counterfeit Monkey by Andrew Plotkin, solving a glaring bug which I didn't catch since I didn't test the more complicated invocations of those phrases. Version 5/210322 was updated by Nathanael Nerode to omit unnecessary code when working with Room Description Control by Emily Short. Version 5/171007 of Large Game Speedup was updated by Nathanael Nerode, adding the "Parts" section. Version 5/171006 of Large Game Speedup was updated by Nathanael Nerode for Inform 6M62 and the responses system. Code was simplifed by using the "choose row I in Table" syntax. Version 4/140731 of Large Game Speedup was written by Andrew Plotkin for Inform 6G60 and was not tested with earlier or later releases.

Example: ** Four Hundred Things - A small game with a lot of stuff.

This game contains 400 offstage objects. (We build these 50 at a time, because the compiler refuses to create hundreds at once.) It demonstrates the "static object grouping" option, and the fast idiom for listing contents.

How much does this improve performance? I tested the example below with and without Large Game Speedup. (When removing the extension, I added a definition "a supporter is empty if nothing is on it." I also changed the "*in" and "initially listing" rules back to their standard forms.)

For each command, I list the number of Glulx VM opcodes and the time taken by the command in two interpreters (Glulxe in C, Quixe in JS). Tests on a 2.7GHz iMac.

LOOK in Kitchen:
without: 1013460 cycles (103.647 ms C, 1918 ms JS)
with: 77386 cycles (20.791 ms C, 193 ms JS)

JUMP
without: 54474 cycles (18.451 ms C, 117 ms JS)
with: 43752 cycles (16.606 ms C, 103 ms JS)

EXAMINE MIRROR in Kitchen
without: 75159 cycles (20.602 ms C, 178 ms JS)
with: 45975 cycles (17.757 ms C, 104 ms JS)

LOOK in Game Room
without: 1025383 cycles (105.147 ms C, 1844 ms JS)
with: 105354 cycles (22.663 ms C, 193 ms JS)

As you see, a lag of nearly two seconds (in the Javascript interpreter) is cut to a fraction of a second.

"Four Hundred Things"

Include Large Game Speedup by Andrew Plotkin.

Use static object grouping.

The player carries the bar of soap.

The Kitchen is a room. "You're in the Kitchen. The Bathroom is east, and the Game Room is south."

The table is a fixed in place supporter in the Kitchen.
The description is "The table only shows up in the room description if something is on it."

The backpack is a container in the Kitchen.
Understand "back", "pack" as backpack.
The book is in the backpack.

Rule for writing a paragraph about the table:
     now the table is mentioned;
     if the table is not empty:
         say "A table stands here. On it [is-are a list of things *in the table]."

The apple is in the Kitchen.
The wedge of cheese is on the table.

The mirror is in the Kitchen. "A portable mirror stands to one side."

Check examining the mirror:
     instead say "In the mirror you see [a list of things *in the location]."
Check searching the mirror:
     instead try examining the mirror.

The Bathroom is east of the Kitchen. "This is the Bathroom. If you drop the soap here, you won't be able to see it. (Except in the mirror.)"

The counter is a scenery supporter in the Bathroom.
The description is "This is an ordinary scenery supporter."

After choosing notable locale objects for the Bathroom:
     set the locale priority of the soap to 0.

The Game Room is south of the Kitchen. "Games are piled around you."

A piece is a kind of thing. The king, the pawn, the rook, and the bishop are pieces.
All pieces are in the Game Room.
A card is a kind of thing. The jack, the trey, and the ace are cards.
All cards are in the Game Room.

Rule for initially listing contents:
     initially group pieces together as "chess pieces";
     initially group cards together.

Before grouping together cards:
     say "[listing group size in words] playing cards (".
After grouping together cards:
     say ")".

Instead of jumping:
     say "You jump up and see all the rooms: [the list of rooms]."

A clone is a kind of thing.
There are 50 clones.
There are 50 clones.
There are 50 clones.
There are 50 clones.
There are 50 clones.
There are 50 clones.
There are 50 clones.
There are 50 clones.

Test me with "jump / get cheese, mirror / look / get apple / east / drop soap / put apple on counter / look / look in mirror / w / s / drop all / look".