Max Heiber
Max Heiber

Reputation: 15502

How do I store module-level write-once state?

I have some module-level state I want to write once and then never modify.

Specifically, I have a set of strings I want to use to look things up in later. What is an efficient and ordinary way of doing this?

I could make a function that always returns the same set:

my_set() -> sets:from_list(["a", "b", "c"]).

Would the VM optimize this, or would the code for constructing the set be re-run every time? I suspect the set would just get GCd.

In that case, should I cache the set in the process dictionary, keyed on something unique like the module md5?

Key = proplists:get_value(md5, module_info()), put(Key, my_set())

Another solution would be to make the caller to call an init function to get back an opaque chunk of state, then pass that state into each function in the module.

Upvotes: 2

Views: 66

Answers (1)

RichardC
RichardC

Reputation: 10557

A compile-time constant, like your example list ["a", "b", "c"], will be stored in a constant pool on the side when the module is loaded, and not rebuilt each time you run the expression. (In the old days, the list would have been reconstructed from its elements for each new call.) This goes for all constants no matter how complicated (like lists of lists of tuples). But when you call out to a function like sets:from_list/1, the compiler cannot assume anything about the representation used by the sets module, and the set will be constructed dynamically from that constant list.

While an ETS table would work, it is less efficient for larger constants (like, say, a set or map containing many entries), because an ETS table has the same memory model as a process - data is written and read by copying, as if by sending messages. If the constants are small, the difference between copying them and recreating them locally would be negligible, and if the constants are large, you waste time copying them.

What you want instead is a fairly new feature called Persistent Term Storage: https://erlang.org/doc/man/persistent_term.html (since Erlang/OTP 21). It is similar to the way compile time constants are handled, so there is no copying when looking up a value. (The key could be the name of your module.) Persistent Term is intended as pretty much a write-once-read-many storage - you can update the stored entry, but that's a more expensive operation which may trigger a global GC.

Upvotes: 6

Related Questions