Verfasst am: 17.10.2016, 16:40
Titel: MEX-Funktion auf logical Matrix anwenden
Guten Tag,
ich kämpfe aktuell mit einem Problem, das ich mir noch nicht erklären kann.
Und zwar habe ich eine Matlab-Funktion, die bestimmte Werte einer Bool-Matrix "g" von 0 auf 1 setzt, mit dem aktuellen Matlab-C-Compiler (Microsoft Visual C++ 2015 Professional) in eine Mex-Funktion überführt, der ein der Matlab-Funktion gleichwertiges C-File zugrunde liegt.
g wird in meinen Experimenten von der MEX-Funktion stets unverändert ausgegeben. Eine gleichwertige Matlab-Funktion hingegen beschreibt g wie gewünscht.
Ich vermute ein Problem bei der Vewendung von logical-Variablen, konkret die Zeile mit
ptr_g[pos] = 1;
.
Ich habe mittlerweile entdeckt, dass es reibungslos funktioniert, wenn g ein Double-Array ist. Bei allen davon abweichenden Datentypen erhalte ich bei einmaliger Ausfühung nicht den erwarteten Rückgabewert g bzw. bei Ausführung des gesamten Programmes (Funktion wird mehrere 100x aufgerufen) einen Matlab Systemfehler.
Code:
% Matlab-Funktion function[ g ] = calc_triangle( g, v )
%Algorithmus teilt Dreieck in zwei Teildreiecke:
%eines mit unterer Kante parallel zur x-Achse (bottom-flat),
%eines mit oberer Kante parallel zur x-Achse (top-flat)
v1 = v(1,:);
v2 = v(2,:);
v3 = v(3,:);
%es gilt: v1(2) <= v2(2) <= v3(2) if v2(2) == v3(2) %keine Teilung nötig: Dreieck ist bereits bottom-flat
g = mex_fillBottomFlatTriangle(g, v1, v2, v3);
else if v1(2) == v2(2) %keine Teilung nötig: Dreieck ist bereits top-flat
g = fillTopFlatTriangle(g, v1, v2, v3);
else %allgemeiner Fall: Dreieck in bottom-flat und top-flat aufteilen
v4 = [floor(v1(1) + ((v2(2) - v1(2)) / (v3(2) - v1(2))) * (v3(1) - v1(1))), v2(2)];
g = mex_fillBottomFlatTriangle(g, v1, v2, v4);
g = fillTopFlatTriangle(g, v2, v4, v3);
end end end
% Jetzt ist ptr_g ein Pointer auf ein DOUBLE Array. Der Input ist aber kein DOUBLE! % ptr_g[1] greift dann z.B. auf [ptr_g + 8] zu, nicht auf [ptr_g + 1]
g = mxDuplicateArray(prhs[0]);
% Jetzt werden die Inputs dupliziert, was zeitraubend und überflüssig ist:
v1 = mxDuplicateArray(prhs[1]);
v2 = mxDuplicateArray(prhs[2]);
v3 = mxDuplicateArray(prhs[3]);
% Greife besser auf die Daten der Originale zurück, ansonsten wird es sich kaum lohnen % eine C-Mex-Funktion zu verwenden.
//get pointer
ptr_g = mxGetPr(g);
% !!!! Das ist ein Fehler, wenn der 1. Input kein DOUBLE ist! % Besser:
mxLogical *ptr_g;
ptr_g = (mxLogical *) mxGetData(prhs[0]);
Zur Sicherheit sollte man also unbedingt die Typen der Inputs testen, z.B. mit "mxIsLogical()". Wenn man einen Scalar von den Inputs ausliest, ist
mxGetScalar
geeignet, weil es auch Integer-Werte umwandelt.
Beim Indizieren von Arrays ist es in C notwendig, dass der Typ des Pointers zum Inhalt der Daten passt.
g = mxDuplicateArray(prhs[0]);
% ich habe es nicht geschafft, das zu umgehen. Du hattest vorgeschlagen ptr_g = (mxLogical *) mxGetData(prhs[0]);. Mit diesem Befehl wurde aber in Matlab beim Aufruf der Funktion g_new = mex_funktion(g, andere variablen) komischerweise g verändert (und nicht g_new). Wie kann ich dieses Problem lösen?
ptr_g = (mxLogical *) mxGetData(g);
% Im weiteren Verlauf des Codes möchte ich nun Einträge der Matrix g mit Einsen befüllen, die anfangs alle mit einer 0 belegt waren (daher reicht ja der Datentyp logical). Das mache ich auf folgende Art:
ptr_g[pos] = 1;
% Am Ende möchte ich nun das veränderte mxArray g wieder ausgeben:
Das funktioniert jetzt auch eigentlich. Der Aufruf g_new = mex_funktion(g, andere variablen) beschreibt g_new wie beabsichtigt und der Datentyp ist wie erhofft logical.
Allerdings wird diese Mex-Funktion sehr häufig aufgerufen und nach einigen Durchläufen nimmt der freie physikalische Speicher des PCs stark ab und Matlab wird extrem langsam.
Ich befürchte also, dass im C-Code eine Art Speicherfreigabe am Ende der Funktion notwendig ist (z.B. mxFree oder mxDestroyArray). Ich habe bisher jedoch keine Lösung dieses Problems gefunden.
Mit mxSetData weißt Du dem Array einen Speicherblock zum Speichern der Elemente zu. Der vorher existierende Block wird dabei nicht freigegeben (siehe Dokumentation vom mxSetData). Nun ist g_ptr aber der Speicherblock, der ja bereits vorher im Array verwendet wurde. Also eigentlich wurde der Pointer mit sich selbst überschrieben. Ich hätte erwartet, dass Matlab das später automatisch freigeben kann. Trotzdem:
mxSetData
ist etwas für Leute, die sehr genau wissen, was sie tun und für den normalen Gebrauch nie erforderlich. Lasse es mal weg und berichte, ob das Problem gelöst ist.
Das Input zu duplizieren kostet allerdings ein wenig mehr Zeit als ein neues mit mxCreateNumericMatrix zu erstellen. Die Werte des Input-Arrays dürfen dann nur ausgelesen werden! Geschrieben wird dann nur ins Output-Array, Du bräuchtest also "*ptr_g_in" und einen "*ptr_g_out", die auf 2 verschiedene Arrays zeigen. Direkt ins Input-Array zu schreiben ist eine tückische Sache, die man unbedingt vermeiden sollte, da Matlab's Copy-on-Write Methode das ausschließt:
Wenn man jetzt a und b an ein Mex-Script gibt und die Pointer zu den Daten bekommt, stellt man fest, dass beide auf den selben Speicherbereich zeigen. Wenn Du dann im Mex-File den Wert von a änderst, wird auch b geändert! So kann man sich leicht Bugs einhandeln, die man kaum noch nachvollziehen kann. Deshalb darf man grundsätzlich niemals in einem Mex-File in einen der Inputs etwas hineinschreiben. (Es sei denn, man kennt die undokumentierten Methoden, die das wieder erlauben und den Code viel effizienter machen...)
Zuletzt bearbeitet von Jan S am 16.11.2016, 16:09, insgesamt einmal bearbeitet
vielen Dank für deine ausführliche und sehr zielführende Hilfe! Beide Vorschläge funktionieren super, ich habe mich für Deinen Nachtrag entschieden, da man sich dadurch wie Du bereits geschrieben hast die Variable g spart.
Außerdem erklärt Dein Beispiel, wieso in meiner ursprünglichen Implementierung in Matlab bei g_new = mex_function(g, andere variablen) g und nicht g_new verändert wurde.
@ Harald,
ich habe die Funktionen mit meinem C Halbwissen in Microsoft Visual Studio selber geschrieben und dann aus Matlab heraus kompiliert um insgesamt Zeit zu sparen.
Die Matalb Coder App hatte ich bisher noch gar nicht in Betracht gezogen, vielen Dank für diesen Hinweis!
Einstellungen und Berechtigungen
Du kannst Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen. Du kannst Dateien in diesem Forum posten Du kannst Dateien in diesem Forum herunterladen
MATLAB, Simulink, Stateflow, Handle Graphics, Real-Time Workshop, SimBiology, SimHydraulics, SimEvents, and xPC TargetBox are registered trademarks and The MathWorks, the L-shaped membrane logo, and Embedded MATLAB are trademarks of The MathWorks, Inc.