(frob)tads memory usage and its implications for the server

I want to try running my game on my linux server and was trying to
come up with an estimate of how much system resources I need to plan
to allocate for every player.

I noticed that CPU usage (at least for my game) is negligible, and the
memory footprint is not too bad in the beginning (~5mb at the start),
but it starts growing with player’s every turn and keeps on growing. I
understand that TADS has to allocate memory with every play so that
the user can undo some number of steps.

Is there any other reason for the amount of allocated memory to always
grow? Is there a theoretical limit to such growth? I’d appreciate any
tips.

I also noticed that every page reload dramatically increases the
amount of allocated memory (up to 1MB on a 32-bit Linux system). Is it
normal?

The good thing is that most of that memory seems to lend itself well
to swapping, which probably means it actually doesn’t get used that
much. But I’m just guessing here.

Any thoughts?

By default, frob uses 16 times the memory that is used for UNDO snapshots, compared to the default that is used in all other Tads interpreters. Memory usage will grow more over time because of that. A memory usage of 50MB or even 100MB is not much for a desktop system, and it gives players to ability to UNDO about 100 turns (on average; it depends on how big a difference there is in game state between turns.) But since you want to set up Tads servers, this isn’t a good idea.

This behavior can be controlled through the -u (or --undo-size) option. It takes a multiplier as argument. See “frob -h”.

Do you still observe memory growing if you use “-u 1”?

Btw, memory usage depends a lot on the game. Tads games can allocate gigabytes of memory, if they want. This isn’t something that can be controlled and no limitations are placed on them by the VM. And they can also leak memory, similar to programs written in any other programming language. You can use operating system facilities to limit the amount of memory a frob process is allowed to allocate (the process will be killed if it goes above that.) It can be as simple as a dedicated shell with a ulimit, or you can use cgroups. I have not used these mechanisms myself, though. I just happen to know that they exist :slight_smile:

This behavior can be controlled through the -u (or --undo-size)
option. It takes a multiplier as argument. See “frob -h”. Do you
still observe memory growing if you use “-u 1”?

That’s a great tip; thanks for the advice! I’ll run more tests and
post the results.

Btw, memory usage depends a lot on the game. Tads games can allocate
gigabytes of memory, if they want. This isn’t something that can be
controlled and no limitations are placed on them by the VM. And
they can also leak memory, similar to programs written in any other
programming language.

Is there a discussion somewhere of bad practices in TADS programming
that can cause memory leaks? I know TADS3 has a GC, but I also know
that memory leaks can be a problem even with languages like Java.

FWIW, I tried running my server-side tests using a barebones game with
a couple of rooms and the PC. The memory allocation pattern was
similar to my game. I need to try again with the -u 1 switch.

It can be as simple as a dedicated shell with a ulimit, or you can
use cgroups.

I tried using cgroups to limit the RSS and swap used by every instance
of the game and it worked really great in my tests; I think cgroups is
a marvelous tool. I still haven’t decided, though, whether to use them
on the game server or leave it to the OS to manage the RAM/swap
allocation. It occurred to me that the OS may do a good job swapping
out the unused pages for a long-lived game so that newer processes are
not starved of RAM, as long as I limit the number of processes that
can be run simultaneously.

On the other hand, making it fair for every process by limiting the
per-process RSS with cgroups may be better - this way new users are
not penalized by long-running games as only the latter ones slow down
due to having to swap more. Need to run more tests to decide.

Thanks a lot for your great work, BTW! :slight_smile:

I have vague memories of seeing profiling code in the TADS3 VM. Maybe you can use it to trace the memory usage.

Also, tads.org/t3doc/doc/sysman/t3vm.htm#t3RunGC can be helpful, maybe.

I have vague memories of seeing profiling code in the TADS3
VM. Maybe you can use it to trace the memory usage.

That’s a great idea; I’ll be looking into that for sure!

For now I found that RealNC’s advice to set --undo-size to 1
really helps: with my game memory usage went down to more
manageable levels. For example, it took about 50 turns to hit 10MB for
the RSS with the default setting. With -u 1 it took a couple of
thousand :slight_smile:

What’s bothering me is why complete page reloads (e.g. pressing F5),
increase the RSS so drastically (by up to 500Kb a pop).

Well, hopefully players won’t be doing much of that, since TAD webui
uses ajax :slight_smile:

Also, tads.org/t3doc/doc/sysman/t3vm.htm#t3RunGC can be helpful, maybe.

I tried running that on every turn by placing the call in a daemon; it
didn’t seem to affect memory usage in my case.

Are there are ways to reduce memory usage even more by maybe disabling
things in the VM code and recompiling? Like completely disabling
undos, for example? :slight_smile:

No, that’s not possible. Though you can reduce static memory use by a bit, by building frobtads using the “-Os” GCC optimization option rather than “-O2”. Add that to both CFLAGS as well as CXXFLAGS. For example:

CFLAGS="-pipe -mtune=native -march=native -Os -fomit-frame-pointer" CXXFLAGS="-pipe -mtune=native -march=native -Os -fomit-frame-pointer" ./configure

This is going to reduce the size of the executable itself. Has no effect on dynamic memory use.

That is a problem. You might want to file a bug for this on bugdb.tads.org

I’ll run more tests to make sure it’s not me and file a bug report.

Thanks, RealNC!

UPD: bugdb.tads.org/view.php?id=222