Opened 4 years ago

Last modified 3 years ago

#6429 new defect

[MM] Add listFlatten and listFlattenReverse in C for more performance

Reported by: Adrian Pop Owned by: Martin Sjölund
Priority: high Milestone:
Component: MetaModelica Version: 1.18.0-dev
Keywords: Cc: Per Östlund

Description

Currently List.flatten is implmeneted as:

listAppend(lst for lst in listReverse(inList))

which has rather bad performance (time and memory wise).

Implement this in C.

Change History (9)

comment:1 by Adrian Pop, 4 years ago

Cc: Per Östlund added
Version: 1.16.21.18.0-dev

comment:2 by Martin Sjölund, 4 years ago

Is it really that bad? You need to create the full list anyway, and it's performing only the listReverse more than the necessary number?

comment:3 by Adrian Pop, 4 years ago

If you have 20 lists it will create 19 lists in the process via each listAppend.
If you do it in C you will create one.

comment:4 by Martin Sjölund, 4 years ago

No, it will only create 1 list:

        while(1) {
          tmp1 = 1;
          if (!listEmpty(_lst_loopVar)) {
            _lst = MMC_CAR(_lst_loopVar);
            _lst_loopVar = MMC_CDR(_lst_loopVar);
            tmp1--;
          }
          if (tmp1 == 0) {
            __omcQ_24tmpVar54 = _lst;
            __omcQ_24tmpVar55 = listAppend(__omcQ_24tmpVar54, __omcQ_24tmpVar55);
          } else if (tmp1 == 1) {
            break;
          } else {
            MMC_THROW_INTERNAL();
          }
        }

listAppend only allocates memory for the first argument; the second argument is kept verbatim.

comment:5 by Adrian Pop, 4 years ago

Then we thought wrong, I thought it would be like one listAppend for each 2 elements in the list of lists, yes the second one is kept intact but on the next iteration it would be copied also.

comment:6 by Adrian Pop, 4 years ago

No, is actually like I said, you lose a lot of memory on this. In C you would allocate once based on the length of the lists and then just copy the elements of all the lists except the last one.

comment:7 by Martin Sjölund, 4 years ago

It's going to be the same with just listAppend :)

listAppend({a},listAppend({b},{c}));

Would allocate a cell for b to create tmp=b::{c} (the original c).
And then allocate a cell for a to create tmp2=c::tmp.

The difference is the overhead of allocations in libgc. But I don't think this is a huge difference in memory cost. You get some locality though (and you won't collect any part of the list until none of it is used).

comment:8 by Martin Sjölund, 4 years ago

Of course, it will not allocate each element since listAppend already does the listLength thing internally (compared to manually creating the list using :: in MetaModelica).

comment:9 by Francesco Casella, 3 years ago

Milestone: 1.18.0

Ticket retargeted after milestone closed

Note: See TracTickets for help on using tickets.