mirror of
https://github.com/alexmchale/merc-mud
synced 2026-08-12 12:26:04 -04:00
137 lines
5.8 KiB
Text
137 lines
5.8 KiB
Text
Merc Release 2.1
|
|
Sunday 01 August 1993
|
|
|
|
Furey mec@shell.portal.com
|
|
Hatchet hatchet@uclink.berkeley.edu
|
|
Kahn michael@uclink.berkeley.edu
|
|
|
|
|
|
|
|
=== Memory Management Overview
|
|
|
|
Merc contains the following memory regions: run file memory; stack; string
|
|
space; and permanent space. The approximate sizes of these regions are:
|
|
|
|
Run file memory 300 k
|
|
Stack 20 k
|
|
String space 1020 k
|
|
Permanent space 1020 k
|
|
TOTAL 2360 k
|
|
|
|
|
|
|
|
=== Run File Memory
|
|
|
|
The merc executable file has about 256k of code, 8k of initialized data, and
|
|
32k of uninitialized data. This was measured on a Sun 4 running SunOS 4.1.3
|
|
with gcc version 2.3.3.
|
|
|
|
This size is small compared to the total size, and there is little you can do
|
|
to change it.
|
|
|
|
|
|
|
|
=== Stack
|
|
|
|
We have never measured stack usage, but estimate it's somewhere between 10k and
|
|
30k. Merc has a wide, shallow calling tree. The largest consumers of stack
|
|
memory are local buffers of size MAX_STRING_LENGTH.
|
|
|
|
If you port Merc to a machine with 64k or less of stack, you should instrument
|
|
your object code with stack-usage measurements. Most compilers for machines in
|
|
this range have an option to do this.
|
|
|
|
|
|
|
|
=== String Space and fread_string()
|
|
|
|
At boot time, the function fread_string() is used to read in ~-delimited
|
|
strings from area files. These strings are stored into string space. After
|
|
all of the area files are read in, string space is read-only for the duration
|
|
of the program.
|
|
|
|
String space is allocated once on bootup and is not expandable. As
|
|
distributed, Merc 2.1 uses 1024k of string space. You can lower or raise
|
|
this number by editing the value of 'MAX_STRING' in 'db.c'.
|
|
|
|
Duplicate strings are stored only once into string space; hashing is used to
|
|
achieve this. The hash code for a string is simply its length. Each string in
|
|
string space is prefaced with a backpointer to the previous string with the
|
|
same hash code. (Aliasing through a union is needed to read and write these
|
|
pointers, because they can start at any byte boundary, and thus are
|
|
misaligned).
|
|
|
|
When fread_string() reads a new string, it creates the string at the end of
|
|
string space. It then finds the first hash pointer for that length and follows
|
|
the thread of hash pointers, looking for an already existing string with the
|
|
same contents. Strings in the hash chain are checked newest-first, which
|
|
benefits from the usual construction of area files, where duplicates are
|
|
usually duplicates of something seen recently.
|
|
|
|
If fread_string() finds that the new string is a duplicate, it simply discards
|
|
the new string and returns a pointer to the existing string. If the string is
|
|
new, it leaves it at the end of string space, fills in the new hash pointers,
|
|
and returns the new string.
|
|
|
|
After area-file loading is over, fread_string is also used to read player
|
|
files. In this case, new strings are not appended to string space, but instead
|
|
are simply handed to str_dup(). Thus the amount of string space used is fixed
|
|
at area-file loading time.
|
|
|
|
The functions str_dup() and free_string() take advantage of string space. If a
|
|
string being duplicated is in string space, str_dup() simply returns the same
|
|
pointer. If a string being freed is in string space, free_string() simply does
|
|
nothing. This strategy not only saves megabytes of memory through string
|
|
re-use, but also saves processor time as well.
|
|
|
|
Thus a cheap method is needed for deciding whether a given string is in string
|
|
space. This is one reason why strings are stored in one contiguous region of
|
|
size MAX_STRING. (The hash chains, however, would still work with
|
|
noncontiguous allocation).
|
|
|
|
Collecting all strings into one region has another advantage: locality of
|
|
reference. Most strings are unused most of the time. Because they are not
|
|
interspersed with other dynamically allocated memory, a virtual memory
|
|
operating system can page out almost all of the string space without affecting
|
|
performance.
|
|
|
|
After boot time the unused end of string space is wasted. This amounts to
|
|
about 160k in Merc 2.1. On a VM system this space is unused space is paged
|
|
out quickly, so it doesn't hurt to leave it alone. However, you can recover
|
|
this space easily by lowering MAX_STRING. If perchance you set MAX_STRING too
|
|
low you will get a boot-time error, but never any operational errors.
|
|
|
|
If you add a lot of areas you may need to increase MAX_STRING. Again, Merc
|
|
will tell you when you've run out.
|
|
|
|
|
|
|
|
=== Permanent space
|
|
|
|
This space holds everything else: all of the area file non-string data; all of
|
|
the created mobiles and objects; descriptor and player character structures;
|
|
you name it.
|
|
|
|
This space is dynamically allocated from calloc() in large blocks, which are
|
|
carved up and handed out as needed by alloc_perm(). As distributed, Merc 2.1
|
|
uses about 1024k of permanent space.
|
|
|
|
Unlike string space, permanent space grows on demand; alloc_perm() calloc's and
|
|
carves up as many blocks as needed as the game runs.
|
|
|
|
You can adjust the block size by changing MAX_PERM_BLOCK in 'db.c'. Adjustment
|
|
will have only a minor effect on space and time usage. If you port Merc to a
|
|
new system with a 64k or 32k limit on calloc() size, you could simply change
|
|
MAX_PERM_BLOCK to the largest calloc'able block on your system.
|
|
|
|
Merc never calls free() to return memory. Instead, memory is recycled for
|
|
future use. This has two advantages: it's faster to recycle a block than to
|
|
call free(), and it's faster to allocate a block from a free list than to
|
|
allocate one from calloc() or alloc_perm(). Most of the structure types have
|
|
their own free lists: char_free, obj_free, affect_free, et cetera.
|
|
|
|
Variable-sized blocks don't fit the per-structure-type free-list model. The
|
|
functions alloc_mem() and free_mem() manage blocks of arbitrary size. They
|
|
actually work by maintaining fixed-size lists for just a few supported sizes
|
|
and rounding requests up to a supported size. This is extremely fast; because
|
|
there are so few variable-sized blocks, the space penalty is inconsequential.
|