Library Link - High Performance IO 
Tips & Tricks

Rafał Chojna, WRI

Join the Conversation #WolframTechConf

Wolfram Language vs the World

Wolfram Language vs the World

LibraryLink Passing Modes

The usual call:

LibraryFunctionLoad["MyLibrary","MyFunction",{(Subscript[Type, 1]),...,(Subscript[Type, n])},ResType];

If you want (Subscript[Type, i]) to be a flat Tensor of integers, you can specify one of:

LibraryLink Passing Modes

Off[LibraryFunction::overload]

fAuto=LibraryFunctionLoad["demo","demo1_T_T",{{Integer,1}},{Integer,_}];

fConst=LibraryFunctionLoad["demo","demo1_T_T",{{Integer,1,"Constant"}},{Integer,_}];

fShared=LibraryFunctionLoad["demo","demo1_T_T",{{Integer,1,"Shared"}},{Integer,_}];

DLLEXPORT int demo1_T_T(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) {
	...
	//The result is going to have all the elements, also the length of each dimension, number of elems, rank and type.
	mint len = num + rank + 3;
  
	err = libData->MTensor_new(type, 1, &len, &T_res);
	if (err) return err;
  
  	intData = libData->MTensor_getIntegerData(T_arg);
	intDataNew = libData->MTensor_getIntegerData(T_res);

	for (mint i = 0; i < num; i++) {
		intDataNew[i] = intData[i];
	}
	for (mint i = 0; i < rank; i++) {
		intDataNew[i+num] = dims[i];
	}
	
	intDataNew[num + rank] = num;
	intDataNew[num + rank + 1] = rank;
	intDataNew[num + rank + 2] = type;
   	
   	MArgument_setMTensor(Res, T_res);
  
	return LIBRARY_NO_ERROR;
  }

LibraryLink Passing Modes

Off[LibraryFunction::overload]

fAuto=LibraryFunctionLoad["demo","demo1_T_T",{{Integer,1}},{Integer,_}];

fConst=LibraryFunctionLoad["demo","demo1_T_T",{{Integer,1,"Constant"}},{Integer,_}];

fShared=LibraryFunctionLoad["demo","demo1_T_T",{{Integer,1,"Shared"}},{Integer,_}];

plotData=
With[{testData=RandomInteger[100,100000*#]},
First/@{
RepeatedTiming[fAuto[testData];],
RepeatedTiming[fConst[testData];],
RepeatedTiming[fShared[testData];]}
]&/@Range[1,400,20];

ListPlot[plotData\[Transpose],PlotLegends->{"Automatic","Constant","Shared"},PlotLabel->"Efficiency of different passing modes",AxesLabel->{"# of elems","Time [ s ]"},DataRange->100000*{1,400}]

Word of caution

Do not overload library functions by Tensor passing mode.

In WL:

manual=LibraryFunctionLoad["MyLibrary","MyFunction",{{Integer,1,"Manual"}},Integer];

const=LibraryFunctionLoad["MyLibrary","MyFunction",{{Integer,1,"Constant"}},Integer];

In “MyLibrary”:

int MyFunction (WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
 	MTensor t = MArgument_getMTensor(Args[0]);
 	
 	// do something with t
 	
 	...
 	
 	// cleanup time - free t or not?
 	
 	return LIBRARY_NO_ERROR; 
}

LibraryLink Return Modes

Passing and return modes work for:

MTensor

MSparseArray

MImage

MRawArray

Mysterious undocumented RawArray

In WolframLibrary.h, have you noticed:

typedef union {
	mbool*	boolean;
	mint*	integer;
	mreal*	real;
	mcomplex*	cmplex;
	MTensor*	tensor;
	MSparseArray*	sparse;
	MRawArray*	raw;
	MImage*		image;
	char**	utf8string;
} MArgument;

Whereas on the official LibraryLink reference page:

Mysterious undocumented RawArray

Some users have noticed:

RawArray

Memory efficient version of Tensor. Supported in LibraryLink as MRawArray.

[RawArray has not been documented yet and its design may change in the future]

r=RawArray["UnsignedInteger8",{2,3,5,7,11}]

Normal[r]

ByteCount[r]

{2,3,5,7,11}//ByteCount

With[{t=RandomInteger[255,100]},ListPlot[AssociationThread[{"Tensor","RawArray"},Map[ByteCount,{Take[t,#]&/@Range[1,50,2],RawArray["UnsignedInteger8",Take[t,#]]&/@Range[1,100,2]},{2}]],PlotLabels->Automatic,PlotLegends->None,PlotLabel->"Container size comparison",AxesLabel->{"Number of elems","Size in Bytes"},DataRange->{0,100},GridLines->{None,{100,200}}]]

RawArray

Supported data types:

TextGrid[{{Style["Mathematica type",Bold,RGBColor[0.45098, 0.223529, 0.678431]],"Integer8","UnsignedInteger8","Integer16","UnsignedInteger16","Integer32","UnsignedInteger32","Integer64","UnsignedInteger64","Real32","Real64","Complex64","Complex128"},{Style["LibraryLink type",Bold,RGBColor[0.45098, 0.223529, 0.678431]],"MRawArray_Type_Bit8 (= 1)","MRawArray_Type_Ubit8","MRawArray_Type_Bit16","MRawArray_Type_Ubit16","MRawArray_Type_Bit32","MRawArray_Type_Ubit32","MRawArray_Type_Bit64","MRawArray_Type_Ubit64","MRawArray_Type_Real32","MRawArray_Type_Real64","MRawArray_Type_Float_Complex","MRawArray_Type_Double_Complex"}}\[Transpose],Dividers->{{2->Gray},{2->Gray}},Frame->True,FrameStyle->Gray,Alignment->{Center,Center},Spacings->{2,1}]

RawArray

MRawArray C interface:

typedef struct st_WolframRawArrayLibrary_Functions {

// MRawArray management
	int  (*MRawArray_new)(rawarray_t, mint, mint const*, MRawArray*);
	void (*MRawArray_free)(MRawArray);
	int  (*MRawArray_clone)(MRawArray, MRawArray*);
	void (*MRawArray_disown)(MRawArray);
	void (*MRawArray_disownAll)(MRawArray);
	mint (*MRawArray_shareCount)(MRawArray);

// MRawArray metadata access
	mint (*MRawArray_getRank)(MRawArray);
	mint (*MRawArray_getFlattenedLength)(MRawArray);
	mint const* (*MRawArray_getDimensions)(MRawArray);
	rawarray_t  (*MRawArray_getType)(MRawArray);
	
// MRawArray data access
	void* 	  (*MRawArray_getData)(MRawArray);
	MRawArray (*MRawArray_convertType)(MRawArray, rawarray_t);

} *WolframRawArrayLibrary_Functions;

LibraryLink vs Modern C++

LibraryLink vs Modern C++

LibraryLink vs Modern C++

LibraryLink vs Modern C++

LibraryLink Utilities for C++

Object-oriented interface

>> LibraryLink

#define  MType_Integer 2
#define  MType_Real 3
#define  MType_Complex 4
	
int  MTensor_new(mint, mint, mint const*, MTensor*);
void MTensor_free(MTensor);
int  MTensor_clone(MTensor, MTensor*);

mint MTensor_getType(MTensor);
mint MTensor_getRank(MTensor);
mint MTensor_getFlattenedLength(MTensor);

int  MTensor_setInteger(MTensor, mint*, mint);
int  MTensor_getInteger(MTensor, mint *, mint*);
mint* MTensor_getIntegerData(MTensor); 
// similarly for real and complex ...

>> LibraryLink Utilities

template<typename T>
class Tensor {
	Tensor();
	~Tensor();
	Tensor(const Tensor&);
	mint getRank();
	mint getFlattenedLength();
	T operator[](mint);
	T& operator[](mint) const;
	T* getData();
};

LibraryLink Utilities for C++

LibraryLink is great for writing C libraries that communicate with Mathematica. What about modern C++?

We are working on a set of utilities that provide modern interface to LibraryLink.

Automatic resource management

>> LibraryLink

int MRawArray_new(rawarray_t, mint, mint const *, MRawArray *);
int MTensor_new(mint, mint, mint const *, MTensor *);
... 
void MTensor_free(MTensor);
void MRawArray_free(MRawArray);
void UTF8String_disown(char*);

>> LibraryLink Utilities

RAII - Resource Acquisition Is Initialization

Tensor::Tensor(...);
RawArray::RawArray(...);
...
Tensor::~Tensor();
RawArray::~RawArray();

LibraryLink Utilities for C++

LibraryLink is great for writing C libraries that communicate with Mathematica. What about modern C++?

We are working on a set of utilities that provide modern interface to LibraryLink.

Error handling

>> LibraryLink

err = MTensor_new(..., t);
if (err) {
	// do cleanup and return error
}
err = MTensor_setInteger(t, 2, 5);
if (err) {
	// do cleanup and return error
}

>> LibraryLink Utilities

try {

	Tensor t {...};
	t[2] = 5; 	// throws if index is out-of-bound 
	
} catch (LibraryLinkException& err) {
	// handle the exception
}

LibraryLink Utilities for C++

LibraryLink is great for writing C libraries that communicate with Mathematica. What about modern C++?

We are working on a set of utilities that provide modern interface to LibraryLink.

Iterators for LibraryLink containers

>> LibraryLink

mint len = MTensor_getFlattenedLength(t);
mint* rawData = MTensor_getIntegerData(t);
long sum = 0;
for (mint i = 0; i < len; ++i) {
 	sum += rawData[i];
}

>> LibraryLink Utilities

long sum = 0;
for (auto& elem : t) {
	sum += elem;
}

// or

auto sum = std::accumulate(t.begin(), t.end(), 0L);

LibraryLink Utilities for C++

Type safety

>> LibraryLink

Type mismatches can only be detected at run-time:

void swapFirst(MTensor t, MTensor s) {
	mint typeT = MTensor_getType(t);
 	mint typeS = MTensor_getType(s);
 	if (typeT != typeS) {
 	 	// whoops 	
 	} 	
 	if (typeT == MType_Integer) {
 	 	mint t1, s1; 
 	 	MTensor_getInteger(t, 1, &t1); 
 	 	MTensor_getInteger(s, 1, &s1); 
 	 	MTensor_setInteger(t, 1, s1); 
 	 	MTensor_setInteger(s, 1, t1); 	
 	} else if (typeT == MType_Real) {
 	 	... 
 	}

>> LibraryLink Utilities

By making Tensor data type a template parameter we get support from the compiler and type mismatches may be detected at compile-time:

template<typename T>
void swapFirst(Tensor<T>& t, Tensor<T>& s) {
 	std::swap(t[0], s[0]); 
}

Learn more

LibraryLink tutorial @ Wolfram Documentation Center

https://reference.wolfram.com/language/LibraryLink/tutorial/Overview.html

C/C++ interface guide @ Wolfram Documentation Center

https://reference.wolfram.com/language/guide/CLanguageInterface.html

Wolfram Research @ GitHub

https://github.com/WolframResearch

Mathematica @ StackExchange

https://mathematica.stackexchange.com

Wolfram Community

https://community.wolfram.com/