5
$\begingroup$

The Catenate is very slow in FunctionCompile. So I want to use the librarylink to make a .dll function to replace it. This is the current c++ code:

#include<iostream>
#include<vector>
#include<algorithm>
#include<numeric>
#include <limits>
#include "WolframCompileLibrary.h"


using namespace std;

struct ManagedArray {
    MTensor* data;
    mint length;
};

struct FixedArray {
    ManagedArray data;
    mint refcount;
};

struct RaggedTensor {
    FixedArray* data;
    mint refcount;
public:
    mint size() {
        return data->data.length;
    }

    MTensor* begin() {
        return data->data.data;
    }

    MTensor* end() {
        return this->begin() + this->size();
    }

    mint nelems() {
        mint len = 0;
        for (auto tensor : *this) {
            len += tensor->nelems;
        }
        return len;
    }

    MTensor toMTensor() {
        mint dims[] = { this->nelems() };
        MTensor Res = new st_MNumericArray[1];
        Res->dims = dims;
        Res->prec = -numeric_limits<mreal>::infinity();
        Res->rank = 1;
        Res->tensor_property_type = 3;
        Res->flags = UBIT32(131);
        Res->data_type = 10;
        Res->nelems = this->nelems();
        Res->data = (void*) new mreal[Res->nelems];
        auto pRes = (mreal*)Res->data;
        for (auto tensor : *this) {
            auto ptensor = (mreal*)tensor->data;
            copy(ptensor, ptensor + tensor->nelems, pRes);
            pRes += tensor->nelems;
        }
        return Res;
    }
};

EXTERN_C DLLEXPORT MTensor Catenate(RaggedTensor inlis){
    inlis.toMTensor();
    (inlis.refcount)++;
    return inlis.toMTensor();
}

But the crazy thing is that I found that the .dll compiled by Debug mode is fine: enter image description here

dec = LibraryFunctionDeclaration[fun -> "Catenate", 
   "D:/programme/cplus2022/Project1/x64/Debug/Project1.dll", {"ListVector"::["PackedArray"::["Real64", 1]]} -> 
    "PackedArray"::["Real64", 1]];
fcplus = FunctionCompile[dec, Function[{Typed[raglis, 
     "ListVector"::["PackedArray"::["Real64", 1]]]}, fun[raglis]]]
data = N[{{5, 9, 3}, {7, 9}, {9, 11, 8, 33}}];
Table[fcplus[data], 10] // Column

enter image description here

But when I compile it by Release mode, The MMA kernel will crash: enter image description here

dec = LibraryFunctionDeclaration[fun -> "Catenate", 
   "D:/programme/cplus2022/Project1/x64/Release/Project1.dll", {"ListVector"::["PackedArray"::["Real64", 1]]} -> 
    "PackedArray"::["Real64", 1]];
fcplus = FunctionCompile[dec, Function[{Typed[raglis, 
     "ListVector"::["PackedArray"::["Real64", 1]]]}, fun[raglis]]]
data = N[{{5, 9, 3}, {7, 9}, {9, 11, 8, 33}}];
Table[fcplus[data], 10] // Column

What's happen? It's a bug of the kernel? Is there any workaround to avoid it?


BTY, the CCompilerDriver`CreateLibrary just can compile code with Release mode even if you add an option Debug -> True as I know.

$\endgroup$
13
  • $\begingroup$ Since you're on Visual Studio, can you confirm the configurations for Release mode are the same as Debug mode? $\endgroup$
    – Ben Izd
    Aug 2, 2022 at 9:23
  • $\begingroup$ @BenIzd I'm pretty sure. Is the Debug and Release both working on your pc? If you can solve this problem, I am willing to give you another 500 points. :) $\endgroup$
    – yode
    Aug 2, 2022 at 9:26
  • $\begingroup$ Mine crashed too, I was as surprised as you, but we're not the first to see this behaviour. It seems uninitialized variables in release and debug mode in Visual Studio are different. I did manage to make the Release mode work by adding Res->refcount = 2;. Note that with/without change the code above leaks memory (also I couldn't understand inlis.toMTensor(); existence) $\endgroup$
    – Ben Izd
    Aug 3, 2022 at 5:50
  • $\begingroup$ All of these aside, on a large ragged list, your code (release mode) was almost 10 times faster than Catenate. Kudos to you. $\endgroup$
    – Ben Izd
    Aug 3, 2022 at 5:55
  • 4
    $\begingroup$ I removed the "bugs" tag. Writing C code that crashes the kernel is very easy to do but in no way is this a Mathematica bug. $\endgroup$
    – Jason B.
    Aug 3, 2022 at 12:23

2 Answers 2

4
$\begingroup$

When I use WolframLibraryData to initialize the MTensor now, I have solved the crash bug. And don't leak memory anymore:

Needs["CCompilerDriver`"]
lib=CCompilerDriver`CreateLibrary["
#include<vector>
#include \"WolframRTL.h\"

using namespace std;

WolframLibraryData libData = WolframLibraryData_new(WolframLibraryVersion);

struct ManagedArray {
    MTensor* data;
    mint length;
};

struct FixedArray {
    ManagedArray data;
    umint refcount;
};

struct RaggedTensor {
    FixedArray* data;
    umint refcount;
public:
    mint size() {
        return data->data.length;
    }

    MTensor* begin() {
        return data->data.data;
    }

    MTensor* end() {
        return this->begin() + this->size();
    }

    mint total_nelems() {
        mint len = 0;
        for (auto tensor : *this) {
            len += tensor->nelems;
        }
        return len;
    }

    template <typename T>
    MTensor toFlattenMTensor() {
        mint dims[] = { this->total_nelems() };
        MTensor Res;
        libData->MTensor_new(MType_Real, 1, dims, &Res);
        auto pRes = (T*)Res->data;
        for (MTensor tensor : *this) {
            auto ptensor = (T*)tensor->data;
            copy(ptensor, ptensor + tensor->nelems, pRes);
            pRes += tensor->nelems;
        }

        return Res;
    }
};

EXTERN_C DLLEXPORT MTensor Catenate(RaggedTensor inlis) {
    WolframLibraryData_free(libData);
    return inlis.toFlattenMTensor<mreal>();
}","Catenatefile","Language"->"C++","Libraries"->"WolframRTL_Kernel.lib"];

dec=LibraryFunctionDeclaration[fun->"Catenate",lib,{"ListVector"::["PackedArray"::["Real64",1]]}->"PackedArray"::["Real64",1]];
fcplus=FunctionCompile[dec,Function[{Typed[raglis,"ListVector"::["PackedArray"::["Real64",1]]]},fun[raglis]]]

Now we can use it now. It's nearly 5 times faster than the built-in Catenate:

lis = Table[ResourceFunction["RandomSplit"][RandomReal[255, 1000], 10], 100000];
AbsoluteTiming[a = Catenate /@ lis;]
AbsoluteTiming[b = fcplus /@ lis;]
a == b

{2.37345, Null}

{0.348485, Null}

True

$\endgroup$
1
2
$\begingroup$

Add a librarylink version, which based on DataStore. It don't crash and doesn't leak memory anymore:

Needs["CCompilerDriver`"]
lib=CCompilerDriver`CreateLibrary["
#include \"WolframIOLibraryFunctions.h\"
#include \"WolframCompileLibrary.h\"

EXTERN_C DLLEXPORT int Catenate(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
    DataStore ds_in = MArgument_getDataStore(Args[0]);
    DataStoreNode dsn = libData->ioLibraryFunctions->DataStore_getFirstNode(ds_in);

    mint n = 0;
    while (dsn != nullptr) {
        MArgument node_data;
        libData->ioLibraryFunctions->DataStoreNode_getData(dsn, &node_data);

        MTensor lis = MArgument_getMTensor(node_data);
        n += lis->nelems;

        dsn = libData->ioLibraryFunctions->DataStoreNode_getNextNode(dsn);
    }

    MTensor result;
    mint dims[] = { n };
    libData->MTensor_new(MType_Real, 1, dims, &result);
    mreal* presult = MTensor_getRealDataMacro(result);
    mint th = 0;

    dsn = libData->ioLibraryFunctions->DataStore_getFirstNode(ds_in);
    while (dsn != nullptr) {
        MArgument node_data;
        libData->ioLibraryFunctions->DataStoreNode_getData(dsn, &node_data);

        MTensor lis = MArgument_getMTensor(node_data);
        mreal* plis = MTensor_getRealDataMacro(lis);

        for (int i = 0; i < lis->nelems; i++) {
            presult[th++] = plis[i];
        }
        dsn = libData->ioLibraryFunctions->DataStoreNode_getNextNode(dsn);
    }

    MArgument_setMTensor(Res, result);
    return LIBRARY_NO_ERROR;
}
","Catenatefile","Language"->"C++"]

fun = LibraryFunctionLoad[lib, "Catenate", {"DataStore"}, {Real, 1}]

It's nearly 5 times faster than the built-in function:

lis=DeleteCases[Table[ResourceFunction["RandomSplit"][RandomReal[255,1000],10],100000],{},{2}];
AbsoluteTiming[b=Catenate/@lis;]
AbsoluteTiming[a=fun[Developer`DataStore@@#]&/@lis;]
a == b

{3.49999, Null}

{0.775869, Null}

True

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.