Print

Print


Hello Jurgen,

Thank you for the quick reply.
I was unware that it was setting the matrix as a copy.
This indeed solves a part of the issue!
I added a new memory overview below, and it saves about 1000 MiB at the end.

I think I am making a similar error when reading out pointers from Ox.
What is the proper way to handle pointers when using for example the FOxCallBack functions?
I use OxStoreCreate and OxStoreDelete for each created pointer, but I think I should be using OxFreeByValue here as well.
I am also using the OX_ARRAY type when returning multiple arguments from a single Ox function. Should this memory be
freed in a specific way as well?

Thank you very much already.
I will keep trying some ideas.

Best,
Alex

Line #    Mem usage    Increment   Line Contents
================================================
    31     25.0 MiB     25.0 MiB     @profile
    32                                 	def test_getterssetters(self):
    33
    34     27.0 MiB      2.0 MiB      	with oxcy.create_ox_environment_from_docstring(self.docstring) as OxEnv:
    35
    36    790.0 MiB    762.9 MiB               	mat1_in = np.ones((10000, 10000))
    37   1552.9 MiB    762.9 MiB               	mat2_in = np.ones((10000, 10000))
    38
    39   6970.1 MiB   5417.2 MiB               	mat1_out, mat2_out = OxEnv.TestGettersSetters(mat1_in, mat2_in)
    40
    41   7084.9 MiB    114.8 MiB               	np.testing.assert_allclose(mat1_in, mat1_out)
    42   7085.1 MiB      0.2 MiB               		np.testing.assert_allclose(mat2_in, mat2_out)
    43
    44   4033.4 MiB  -3051.8 MiB           	del mat1_in, mat2_in, mat1_out, mat2_out
    45
    46   4034.5 MiB      1.2 MiB           	with oxcy.create_ox_environment_from_docstring(self.docstring) as OxEnv:
    47
    48   4797.5 MiB    763.0 MiB               	mat1_in = np.ones((10000, 10000))
    49   5560.4 MiB    762.9 MiB               	mat2_in = np.ones((10000, 10000))
    50
    51   7075.5 MiB   1515.1 MiB               	mat1_out, mat2_out = OxEnv.TestGettersSetters(mat1_in, mat2_in)
    52
    53   7075.5 MiB      0.0 MiB               		np.testing.assert_allclose(mat1_in, mat1_out)
    54   7074.8 MiB     -0.7 MiB               		np.testing.assert_allclose(mat2_in, mat2_out)
    55
    56   4023.0 MiB  -3051.8 MiB           	del mat1_in, mat2_in, mat1_out, mat2_out

Cython snippets:

cdef call_ox_func(str f_name):
    """ call ox static function """
    def func(*args):
        try:
            f_ptr = cox.OxStoreCreate(1)
            cox.OxValSetString(f_ptr, f_name)

            in_ptr = cox.OxStoreCreate(len(args))

            for i in range(len(args)):
                set_ox_var(cox.OxValGetVal(in_ptr, i), args[i])

            out_ptr = cox.OxStoreCreate(1)

            if cox.FOxCallBack(f_ptr, out_ptr, in_ptr, len(args)) != 1:
                raise Exception("Failed to call function!", f_name)

            val = get_ox_var(out_ptr)
        finally:
            cox.OxStoreDelete(f_ptr, 1)
            cox.OxStoreDelete(in_ptr, len(args))
            cox.OxStoreDelete(out_ptr, 1)
        return val
    return func

cdef get_matrix(cox.OxVALUE *ptr):
    """ get matrix from ptr """
    if not cox.OxValHasType(ptr, cox.OxTypes.OX_MATRIX):
        raise ValueError("Expected OX_MATRIX. Received", "?")
    matrix = cox.OxValGetMat(ptr)
    return np.array([
        [cox.MatGetAt(matrix, i, j) for j in range(cox.OxValGetMatc(ptr))] 
        for i in range(cox.OxValGetMatr(ptr))]
        )

cdef set_matrix(cox.OxVALUE *ptr, np.ndarray[np.double_t, ndim=2] m):
    """ write matrix to ptr """
    m = m.astype(float)
    cR, cC = m.shape[0], m.shape[1]
    matrix = cox.MatAllocBlock(cR, cC)
    for i in range(cR):
        for j in range(cC):
            cox.MatSetAt(matrix, m[i, j], i, j)
    cox.OxValSetMat(ptr, matrix, cR, cC)
    cox.MatFreeBlock(matrix)

-----Original Message-----
From: Jurgen Doornik [mailto:[log in to unmask]] 
Sent: Tuesday, July 3, 2018 6:52 PM
To: Alex de Geus <[log in to unmask]>; [log in to unmask]
Subject: Re: Cython-Ox Possible Memory Leak

Dear Alex,

I only had a cursory look, but could the problem be in your set_matrix?

OxValSetMat takes a copy of the matrix, but I don't see a free in set_matrix.

Jurgen

oxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxox
oxox 20th OxMetrics user conference 10-11 Sept 2018 oxox Centre for Econometric Analysis, Cass Business School oxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxox
Dr Jurgen A Doornik
James Martin Fellow, Institute for New Economic Thinking at the Oxford Martin School, University of Oxford http://www.doornik.com oxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxox

On 2018-07-03 11:38, Alex de Geus wrote:
> Hello,
> 
> I am experiencing some large memory consumptions in an application 
> using Cython to communicate with Ox.
> 
> I am using the Ox Professional version 7.10 with the Development Kit 
> with this manual as reference https://www.doornik.com/ox/OxDeveloper.pdf.
> 
> Below are some code snippets with a simplified unit test that hands 2 
> large matrices to ox and back to python again.
> 
> I have tried to clean up all the pointer allocations to Ox very 
> carefully, however this does not seem to work correctly as memory 
> keeps accumulating for every Ox call.
> 
> I am pretty sure I am not freeing the memory allocated correctly for 
> both the input and output arguments, however I have been unsuccessful in finding a solution.
> 
> I have tried the void OxFreeByValue(OxVALUE*) function for the output 
> arguments, but it does not seem to help, in some cases it even increases memory consumption.
> 
> I have tried the void MatFreeBlock(MATRIX) function for the input and 
> output arguments of type matrix, but this always seems to crash.
> 
> Any idea on how to fix this or how to debug this code more effectively?
> 
> Best,
> 
> Alex
> 
> Memory consumption (using the memory_profiler package from python):
> 
> Line #    Mem usage    Increment   Line Contents
> 
> ================================================
> 
>      31     25.1 MiB     25.1 MiB    @profile
> 
>      32                                         def test_getterssetters(self):
> 
>      33
> 
>      34     27.1 MiB      2.1 MiB                 with
> oxcy.create_ox_environment_from_docstring(self.docstring) as OxEnv:
> 
>      35
> 
>      36    790.1 MiB    762.9 MiB                           mat1_in = 
> np.ones((10000, 10000))
> 
>      37   1553.0 MiB    762.9 MiB                          mat2_in = 
> np.ones((10000, 10000))
> 
>      38
> 
>      39   7096.0 MiB   5543.0 MiB                         mat1_out, 
> mat2_out = OxEnv.TestGettersSetters(mat1_in, mat2_in)
> 
>      40
> 
>      41   7096.1 MiB      0.1 MiB                           
> np.testing.assert_allclose(mat1_in, mat1_out)
> 
>      42   7095.4 MiB     -0.7 MiB                           
> np.testing.assert_allclose(mat2_in, mat2_out)
> 
>      43
> 
>      44   4043.7 MiB  -3051.8 MiB              del mat1_in, mat2_in, 
> mat1_out, mat2_out
> 
>      45
> 
>      46   4044.4 MiB      0.8 MiB                with
> oxcy.create_ox_environment_from_docstring(self.docstring) as OxEnv:
> 
>      47
> 
>      48   4807.3 MiB    762.9 MiB                          mat1_in = 
> np.ones((10000, 10000))
> 
>      49   5570.3 MiB    762.9 MiB                          mat2_in = 
> np.ones((10000, 10000))
> 
>      50
> 
>      51   8024.9 MiB   2454.6 MiB                          mat1_out, 
> mat2_out = OxEnv.TestGettersSetters(mat1_in, mat2_in)
> 
>      52
> 
>      53   8079.7 MiB     54.8 MiB                           
> np.testing.assert_allclose(mat1_in, mat1_out)
> 
>      54   8079.2 MiB     -0.5 MiB                            
> np.testing.assert_allclose(mat2_in, mat2_out)
> 
>      55
> 
>      56   5027.4 MiB  -3051.8 MiB              del mat1_in, mat2_in, 
> mat1_out, mat2_out
> 
> Python snippet:
> 
> classTest_oxcy(unittest.TestCase):
> 
> defsetUp(self):
> 
> self.docstring = \
> 
> """
> 
> #include <oxstd.h>
> 
> #include <packages/ssfpack/ssfpack_ex.h>
> 
> TestGettersSetters(const mat1, const mat2);
> 
> TestGettersSetters(const mat1, const mat2)
> 
> {
> 
>      decl output = new array[2];
> 
>      output[0] = mat1;
> 
>      output[1] = mat2;
> 
>      return output;
> 
> }
> 
> """
> 
> deftest_getterssetters(self):
> 
> withoxcy.create_ox_environment_from_docstring(self.docstring) asOxEnv:
> 
>              mat1_in = np.ones((10000, 10000))
> 
>              mat2_in = np.ones((10000, 10000))
> 
>              mat1_out, mat2_out = OxEnv.TestGettersSetters(mat1_in, 
> mat2_in)
> 
>              np.testing.assert_allclose(mat1_in, mat1_out)
> 
>              np.testing.assert_allclose(mat2_in, mat2_out)
> 
> delmat1_in, mat2_in, mat1_out, mat2_out
> 
> withoxcy.create_ox_environment_from_docstring(self.docstring) asOxEnv:
> 
>              mat1_in = np.ones((10000, 10000))
> 
>              mat2_in = np.ones((10000, 10000))
> 
>              mat1_out, mat2_out = OxEnv.TestGettersSetters(mat1_in, 
> mat2_in)
> 
>              np.testing.assert_allclose(mat1_in, mat1_out)
> 
>              np.testing.assert_allclose(mat2_in, mat2_out)
> 
> delmat1_in, mat2_in, mat1_out, mat2_out
> 
> deftearDown(self):
> 
> pass
> 
> if__name__== "__main__":
> 
>      unittest.main()
> 
> Cython snippets:
> 
> cdef classOxEnv:
> 
> """ ox environment wrapper """
> 
>      ...
> 
> def__enter__(self):
> 
> """ load ox source file into ox compiler """
> 
> # TODO: check return value for response?
> 
>          cox.OxMainCmd('-r- {}'.format(self.src))
> 
> returnself
> 
> def__exit__(self, exc_type, exc_message, exc_traceback):
> 
> """ exit ox runtime environment """
> 
>          ...
> 
>          cox.OxRunExit()
> 
>          cox.OxMainExit()
> 
>      ...
> 
> cdefcall_ox_func(strf_name):
> 
> """ call ox static function """
> 
> deffunc(*args):
> 
> try:
> 
>              f_ptr = cox.OxStoreCreate(1)
> 
>              cox.OxValSetString(f_ptr, f_name)
> 
>              in_ptr = cox.OxStoreCreate(len(args))
> 
> fori in range(len(args)):
> 
>                  set_ox_var(cox.OxValGetVal(in_ptr, i), args[i])
> 
>              out_ptr = cox.OxStoreCreate(1)
> 
> ifcox.FOxCallBack(f_ptr, out_ptr, in_ptr, len(args)) != 1:
> 
> raiseException("Failed to call function!", f_name)
> 
>              val = get_ox_var(out_ptr)
> 
> finally:
> 
>              cox.OxStoreDelete(f_ptr, 1)
> 
> cox.OxStoreDelete(in_ptr, len(args))
> 
> cox.OxStoreDelete(out_ptr, 1)
> 
> returnval
> 
> returnfunc
> 
> # called by set_ox_var for matrix types
> 
> cdefset_matrix(cox.OxVALUE *ptr, np.ndarray[np.double_t, ndim=2] m):
> 
> """ write matrix to ptr """
> 
>      cR, cC = m.shape[0], m.shape[1]
> 
> matrix = cox.MatAllocBlock(cR, cC)
> 
> fori in range(cR):
> 
> forj in range(cC):
> 
>              cox.MatSetAt(matrix, m[i, j], i, j)
> 
>      cox.OxValSetMat(ptr, matrix, cR, cC)
> 
> 
> 
> Please consider the environment before printing this document
> 
> ----------------------------------------------------------------------
> ----------
> 
> To unsubscribe from the OX-USERS list, click the following link:
> https://www.jiscmail.ac.uk/cgi-bin/webadmin?SUBED1=OX-USERS&A=1
> 

########################################################################

To unsubscribe from the OX-USERS list, click the following link:
https://www.jiscmail.ac.uk/cgi-bin/webadmin?SUBED1=OX-USERS&A=1