Opened 10 years ago

Last modified 9 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, 10 years ago

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

comment:2 by Henning Kiel, 10 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, 10 years ago

Cc: Francesco Casella added

comment:4 by Henning Kiel, 10 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, 10 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, 10 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, 10 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, 9 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.