4
$\begingroup$

Problem

How to get the head, size, dimensions, etc. of a symbol without evaluating it? Since Mathematica does not allocate fix storage space and type to symbols it is not trivial to query runtime properties of variables. The problem is that one cannot measure e.g. size without evaluating at least a little, though in Mathematica, evaluation could cause all kinds of problems. In certain cases, extra evaluation during query could cause the loss of the original data, other unwanted side effects or even hangup in extreme cases where evaluating the expression is computationally expensive. Let's assume that we are only interested in OwnValues and only in accessing the bytesize of the expression. Imagine you want to check your memory to see what is the size of actual symbol values (like Workspace does in Matlab). For lots of symbols, huge values and non-static expressions a complete scan could easily choke your machine.

Method 1: Sandbox

One possible way is to evaluate the expression in a safe environment that does not allow memory leaks. For example, in a local kernel without the possibility of changing state of the original kernel. This has at least three limitations: unnecessary copying of expressions from kernel to sandbox, inconsistency (might yield different results) and the fact that computationally expensive calculations would still be performed.

Method 2: Evaluate only immutable expressions

Another method is to limit size-queries to already calculated, immutable expressions only, leaving those that can potentially cause side effects untouched. This method would be safe in the sense that it would not try to re-evaluate huge expressions that are not static (being set with e.g. SetDelayed). Though it would require a test whether OwnValues of a symbol would further transform if evaluated or not. As a matter of fact, immutability is not even a necessity: we could measure size for symbols which are:

  1. consistent: evaluate to the same result;
  2. contained: without any side effects;
  3. cheap: possibly without expensive computation.

For example, f:=1+1 could be evaluated to measure size as it is cheap, does not have side effects and does not change the previous value of f. But f:=(x++;RandomReal[]) should not be evaluated for measurement as it has a side effect changing the global state and it would erase any previous value of f.

Simple problem case

Evaluating the following two definitions we assign values to e and f:

e = {1};
f := {Print[0]; 1}
{e == f, e === f}    (* ==> {True, True} *)

It seems that f is numerically and structurally identical to e, thus having actual value {1} but it also has the own value {Print[0]; 1}. How can one measure the memory-requirement of the actual value of f without triggering the re-evaluation of OwnValues?

A more problematic case is the following:

g := Table[1, {RandomInteger@100000}]
{ByteCount@g, ByteCount@OwnValues@g} (*  ==>  {287984, 336} *)

Clearly, g has a certain actual value with a certain size (probably it's different on your machine) that has not much to do with the actual size of its OwnValues. Now any such ByteCount call would of course re-evaluate g which is to be avoided as it might involve unwanted side-effects.

The problem with Method 2 is that there is no safe way (at least that I know of) which could test whether an expression is immutable (assuming it is more complex than an atom; Mr.Wizard offered a step function, but I failed to apply it to this case), consistent, has no side effects and is cheap to evaluate. OwnValues unfortunately does not store how a symbol was set (using Set or SetDelayed) thus one cannot easily exclude symbols that are unsafe to re-evaluate.

Questions

How to measure the bytesize of the value of a "nonstatic" symbol (g) that should not be re-evaluated? Is there a method to ascertain whether an expression can be safely evaluated? Is there a method to safely check whether an expression is immutable? Is there a method to safely check whether an expression is consistent, has no side effects and is cheap to evaluate?

I found the following posts extremely useful:

$\endgroup$
10
  • 1
    $\begingroup$ I am afraid you can't generally measure the memory requirements etc. for delayed definitions, because for them the creation of corresponding values happens only at run-time. $\endgroup$ Commented Jun 11, 2013 at 14:35
  • $\begingroup$ @Leonid Yes, I got to the same conclusion, but then how can one exclude delayed definitions? $\endgroup$ Commented Jun 11, 2013 at 14:50
  • $\begingroup$ You can't. It is a trade-off. If you want to delay some evaluation until run-time, you have to use delayed definitions. If it is the code you write yourself, you can avoid definitions like var:= stuff, and write var[]:=stuff instead - but you have to be consistent with this. If this is someone else's code, you can't do much without developing some sophisticated code analysis tools which would to some extent reconstruct the stronger type system. But even then, things like memory requirements are hard to automatically extract, they can easily be data-dependent (and usually are). $\endgroup$ Commented Jun 11, 2013 at 15:11
  • $\begingroup$ Or you can precompute some stuff and keep it in memory - then you will spend memory but will be able to see how much memory is needed without further evaluation. But in a way this destroys the purpose. $\endgroup$ Commented Jun 11, 2013 at 15:13
  • $\begingroup$ @Leonid If you edit your comments into an answer I'll be glad to accept it (per rm-rf's recent manifesto about decreasing number of unanswered Q-s). $\endgroup$ Commented Jan 10, 2014 at 11:12

1 Answer 1

2
$\begingroup$

As far as I can tell, in general there is no way to do what you want. One can't generally measure the memory requirements etc. for delayed definitions, because for them the creation of corresponding values happens only at run-time. OTOH, you can't really exclude the delayed definitions, particularly when dealing with the code of others.

It is a trade-off. If you want to delay some evaluation until run-time, you have to use delayed definitions. If it is the code you write yourself, you can avoid definitions like

var:= stuff, 

and write

var[]:=stuff 

instead - but you have to be consistent with this. If this is someone else's code, you can't do much without developing some sophisticated code analysis tools which would to some extent reconstruct the stronger type system. But even then, things like memory requirements are hard to automatically extract, they can easily be data-dependent (and usually are).

Or you can precompute some stuff and keep it in memory - then you will spend memory but will be able to see how much memory is needed without further evaluation. But in a way this destroys the purpose.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.