Opened 9 years ago

Last modified 8 years ago

#3814 new enhancement

Optimizsation of record modifications

Reported by: Henning Kiel Owned by: Martin Sjölund
Priority: high Milestone: Future
Component: MetaModelica Version:
Keywords: Cc: Francesco Casella

Description

Consider the following example code:

record exampleLst
  Integer n;
  list<Integer> elems;
  // more elements to follow...
end record;

function appList
  input list<Integer> li;
  input output exampleLst lst;
algorithm
    lst.n := lst.n + listLength(li);
    lst.elems := listAppend(li, lst.elems);
  end with;
end appList;

Currently the compiler will create a new record for each of the two assignment lines (via mmc_mk_box()), though the intermediate object is never really referenced or used.

Now, the compiler could try to figure out this and only do one mmc_mk_box() incorporating both changes. This would immediately reduce overall memory usage.

A second approach would be to extend MetaModelica language with a with <elem> do … end with; construct like the following:

function appList
  input list<Integer> li;
  input output exampleLst lst;
algorithm
  with lst do
    .n := .n + listLength(li);
    .elems := listAppend(.elems, li);
  end with;
end appList;

Here, the assignment (mmc_mk_box()) is only done at the end with; line, and the component contents stored in temporary variables.

I do not bother if one or both ways are implemented, but I would definitely love to see at least one of them :-)

The second approach could also be extended to e.g. also allow for-loops etc.

function appList
  input list<list<Integer>> lists;
  input output exampleLst lst;
algorithm
  for li in lists loop
    with lst do
      .n := .n + listLength(li);
      .elems := listAppend(.elems, li);
     end with;
  end for;
end appList;

Change History (8)

comment:1 by Martin Sjölund, 9 years ago

The second approach is not so good since .n means "lookup n in the global scope".

comment:2 by Henning Kiel, 9 years ago

can we use some other syntax then? I guess the semantics is not problematic.

Maybe "..n" or "%.n" or something else?

comment:3 by Henning Kiel, 9 years ago

Cc: Francesco Casella added

comment:4 by Henning Kiel, 9 years ago

Maybe we should keep the object name:

function appList
  input list<Integer> li;
  input output exampleLst lst;
algorithm
  with lst do
    lst.n := lst.n + listLength(li);
    lst.elems := listAppend(lst.elems, li);
  end with;
end appList;

and implement the above as syntactic sugar for

function appList
  input list<Integer> li;
  input output exampleLst lst;
protected
  Integer lst_n;           // used whenever "lst.n" is referenced in code
  list<Integer> lst_elems; // used whenever "lst.elems" is referenced in code
algorithm
  lst_n := lst.n;                         // with lst do
  lst_elems := lst.elems;
  // use of plain lst inside "with" should produce an error

  lst_n := lst_n + listLength(li);        //   lst.n := lst.n + listLength(li);
  lst_elems := listAppend(lst_elems, li); //   lst.elems := listAppend(lst.elems, li);
  lst := exampleLst(lst_n, lst_elems);    // end with;
end appList;

comment:5 by Henning Kiel, 9 years ago

Optionally, the syntax could be updated to

with <var1>,<var2>,... do
  <code>
end with;

to avoid deeply nested with-clauses, though I doubt that more than two variables will be used simultaneously.

comment:6 by Martin Sjölund, 9 years ago

SimCodeUtil.derVarFromStateVar is a prime example of this. It is very annoying to rewrite this function since there are 23 fields in there and we want to update 8 of them. It takes 25 words to store this variable, which means 1600 bytes of allocation for each call to this function. @casella: this function is called once for each variable in the system (#3813); I will change it to only be called for state variables.

comment:7 by Henning Kiel, 9 years ago

Any activity on this topic? Looks like this is one of the big memory eaters...

What do you think about

protected function derVarFromStateVar
  input SimCodeVar.SimVar state;
  output SimCodeVar.SimVar deriv = state;
algorithm
  with deriv do
    deriv.arrayCref := Util.applyOption(deriv.arrayCref, ComponentReference.crefPrefixDer);
    deriv.name := ComponentReference.crefPrefixDer(deriv.name);
    deriv.varKind := BackendDAE.STATE_DER();
    deriv.initialValue := NONE();
    deriv.aliasvar := SimCodeVar.NOALIAS();
    deriv.causality := SimCodeVar.INTERNAL();
    deriv.variable_index := NONE();
    deriv.isValueChangeable := false;
  end do;
end derVarFromStateVar;

as syntactic sugar for

protected function derVarFromStateVar
  input SimCodeVar.SimVar state;
  output SimCodeVar.SimVar deriv = state;
algorithm
  SIMVAR(name = name, varKind=varKind, .....) := deriv;
    arrayCref := Util.applyOption(arrayCref, ComponentReference.crefPrefixDer);
    name := ComponentReference.crefPrefixDer(name);
    varKind := BackendDAE.STATE_DER();
    initialValue := NONE();
    aliasvar := SimCodeVar.NOALIAS();
    causality := SimCodeVar.INTERNAL();
    variable_index := NONE();
    isValueChangeable := false;
  deriv := SIMVAR(name, varKind, .......);
end derVarFromStateVar;

?

comment:8 by Henning Kiel, 8 years ago

Another idea which does not introduce new keywords. This could also be adapted to accept tuples on the RHS.

protected function derVarFromStateVar
  input SimCodeVar.SimVar state;
  output SimCodeVar.SimVar deriv = state;
algorithm
  deriv.(arrayCref, name, varKind, initialValue, aliasvar, causality, variable_index, isValueChangeable) := (
    Util.applyOption(deriv.arrayCref, ComponentReference.crefPrefixDer),
    ComponentReference.crefPrefixDer(deriv.name),
    BackendDAE.STATE_DER(),
    NONE(),
    SimCodeVar.NOALIAS(),
    SimCodeVar.INTERNAL(),
    NONE(),
    false
  );
end derVarFromStateVar;

Readability is a bit worse than the other aproaches above, but could be easier to implement???

Note: See TracTickets for help on using tickets.