Re: Excessive copies of set elements in GMPL

Domingo Alvarez Duarte <[email protected]> Thu, 16 Jul 2020 18:40:03 +0200
Newsgroups gmane.comp.gnu.glpk
Message-ID <[email protected]>
Hello Andrew !

Thank you for reply !

After doing some experimentation I think I found an easy way to 
eliminate unnecessary set copies here is a commit on 
https://github.com/mingodad/GLPK/commit/6f3da6ab31ca8d710f706f60635e5b17cf2ded40, 
also see bellow, glpsol now uses 1/3 of the memory and is slight faster:

====

set S := {1..1000000};
param d { i in S} := if ((i mod 2) == 0) then 0.0000000123 else 
12345.0000000001;
display card(S);
display sum {i in S} d[i];
display card(S)*2;
display card(S)*3;
display sum {i in S : (i mod 2) == 0} d[i];
display sum {i in S : (i mod 2) <> 0} d[i];
end;

====

/usr/bin/time ./glpsol -m sum-test.mod #without refcount
GLPSOL: GLPK LP/MIP Solver, v4.65
...

Memory used: 645.0 Mb (676328774 bytes)
3.68user 0.23system 0:03.91elapsed 100%CPU (0avgtext+0avgdata 
664568maxresident)k

====

/usr/bin/time ./glpsol -m sum-test.mod #with refcount
GLPSOL: GLPK LP/MIP Solver, v4.65
...

Memory used: 215.4 Mb (225829958 bytes)
3.06user 0.08system 0:03.14elapsed 100%CPU (0avgtext+0avgdata 
223772maxresident)k

====

diff --git a/src/mpl/mpl.h b/src/mpl/mpl.h
index ddd3154..262a783 100644
--- a/src/mpl/mpl.h
+++ b/src/mpl/mpl.h
@@ -1358,6 +1358,8 @@ struct ARRAY
           the array is 0-dimensional */
        int size;
        /* size of the array, i.e. number of its members */
+      int refcount;
+      /* reference counting to allow reuse without copy */
        MEMBER *head;
        /* the first array member; NULL means the array is empty */
        MEMBER *tail;
diff --git a/src/mpl/mpl3.c b/src/mpl/mpl3.c
index 2489db2..036ef8c 100644
--- a/src/mpl/mpl3.c
+++ b/src/mpl/mpl3.c
@@ -1002,6 +1002,7 @@ MEMBER *add_tuple
        xassert(set != NULL);
        xassert(set->type == A_NONE);
        xassert(set->dim == tuple_dimen(mpl, tuple));
+      xassert(set->refcount == 1);
        memb = add_member(mpl, set, tuple);
        memb->value.none = NULL;
        return memb;
@@ -1038,6 +1039,10 @@ ELEMSET *copy_elemset
        xassert(set != NULL);
        xassert(set->type == A_NONE);
        xassert(set->dim > 0);
+      if(set->size) {
+          ++set->refcount;
+          return set;
+      }
        copy = create_elemset(mpl, set->dim);
        for (memb = set->head; memb != NULL; memb = memb->next)
           add_tuple(mpl, copy, copy_tuple(mpl, memb->tuple));
@@ -1170,6 +1175,7 @@ ELEMSET *set_union
        xassert(X != NULL);
        xassert(X->type == A_NONE);
        xassert(X->dim > 0);
+      xassert(X->refcount == 1); /* only add to recently/owned created 
sets */
        xassert(Y != NULL);
        xassert(Y->type == A_NONE);
        xassert(Y->dim > 0);
@@ -1626,6 +1632,7 @@ ARRAY *create_array(MPL *mpl, int type, int dim)
        array->type = type;
        array->dim = dim;
        array->size = 0;
+      array->refcount = 1;
        array->head = NULL;
        array->tail = NULL;
        array->tree = NULL;
@@ -1742,6 +1749,7 @@ void delete_array
  )
  {     MEMBER *memb;
        xassert(array != NULL);
+      if(--array->refcount) return; /* someone is still using this array */
        /* delete all existing array members */
        while (array->head != NULL)
        {  memb = array->head;

====

Cheers !

On 16/7/20 14:06, Andrew Makhorin wrote:
>> Trying to improve GLPK/GMPL
> It is a non-trivial task.
>
> The MathProg translator included in glpk was implemented in a
> non-efficient way, because there was no intention to process models
> of huge size (so this allowed essentially reducing implementation
> efforts).
>
> The key idea to translate MathProg (or AMPL) models much more
> efficiently is to avoid using "direct" representation of sets and
> arrays and implement all operations in a way similar to one used
> in relational database management systems.
>
>> I can see that GMPL model generation is
>> making an excessive number of set element copies (see bellow).
> Please note that most of the copies might be temporary quantities
> that exist (allocated in the memory) only during evaluation of the
> (sub)expressions involving these quantities.
>
>
> Best regards,
>
> Andrew Makhorin