[LINUX] Interprozesskommunikation für Embedded DL

Dieser Artikel ist der Artikel zum 20. Tag von LeapMind Adventskalender 2019.

einpacken

Ich werde darüber schreiben, wie die Zeit verkürzt werden kann, um das Ergebnis des Deep Learning-Modells zu erhalten, indem Socket und Signal für die Kommunikation zwischen Prozessen in C-Sprache verwendet und die Initialisierung im Voraus durchgeführt werden.

Vorstellen

Ich bin normalerweise ein ** Python-basierter Deep Learning-Ingenieur **. Dieses Mal werde ich ** C Sprache ** verwenden. Es gibt viele Ingenieure, die C / C ++ in LeapMind verwenden, das eingebettete DL entwickelt, aber ich selbst entwickle selten mit C / C ++. ** Ich bin nicht sehr kompetent **. Wenn Sie also Fehler haben, lassen Sie es mich bitte wissen!

Zweck

Der Titel lautet Prozessübergreifende Kommunikation für Embedded DL, aber es ist nur Prozessübergreifende Kommunikation! Eigentlich wollte ich mnist mit C / C ++ ausführen, aber da die Menge groß ist, ist der Inferenzteil Dummy (später beschrieben).

Übrigens gibt es bei eingebetteten Geräten wie der Fertigungsindustrie Zeiten, in denen Sie die Verarbeitung in kurzer Zeit durchführen möchten, z. B. ** einige zehn ms **. Wenn die Eingabe klein ist, kann sogar ein DL-Modell in zehn ms ausgeführt werden. Wenn Sie einfach das von TensorFlow erstellte DL-Modell laden, kann der Start ** einige Sekunden ** dauern.

In einem solchen Fall, indem Sie es als Dienst im Voraus starten und eine Anfrage senden, Sie können die Wartezeit vermeiden.

Tatsächlich verfügt Python, das häufig in DL verwendet wird, natürlich über ein Socket-Modul, und es gibt eine Bibliothek, die umfangreicher sein kann. Es gibt viele, aber in einer nahezu eingebetteten Umgebung möchten Sie möglicherweise Python installieren oder nicht. Daher müssen Sie möglicherweise in ** C / C ++ usw. ** entwickeln.

Umgebung

Die diesmal verwendete Umgebung ist wie folgt.

meint

Es gibt mehrere Möglichkeiten, zwischen Prozessen zu kommunizieren. Dieses Mal verwenden wir die Socket-Kommunikation für die Kommunikation zwischen Prozessen. Als Bonus möchte ich auch eine einfache Methode mit Signal schreiben.

Prozessablauf

Die Kommunikationsverarbeitung von Client und Server erfolgt wie in der folgenden Abbildung dargestellt. Geben Sie als Inferenz eine 3x3-Matrix ein und berechnen Sie die Summe für jede Spalte. Eine 3x3-Matrix (9 Float-Spalten) wird vom Client gesendet, damit die 3 Float-Spalten kommunizieren.

Untitled Diagram.png

Quellcode

Es gibt nicht viele Bücher über Sockets. Lesen Sie daher die technischen Blogs im Internet, um sich einen Überblick zu verschaffen, und ** für genaue Informationen ** [** Linux Programmer's Manual (Übersetzung) **](https: / Ich denke, es ist normal, /linuxjm.osdn.jp/html/LDP_man-pages/man7/socket.7.html) zu lesen und zu verstehen.

Referenzartikel / Materialien

server.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define PORT 52579
#define SIZE 3


void inference(float* input, float* output)
{
    for (size_t i=0; i<SIZE; ++i)
    {
        for (size_t j=0; j<SIZE; ++j)
        {
            output[i] += input[SIZE*i + j];
        }
    }
}


int main(void)
{
    // create socket
    int sock;
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        perror("socket");
        return 1;
    }

    // struct about the connection destination
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = INADDR_ANY;

    // bind
    if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
    {
        perror("bind");
        return 1;
    }

    // listen
    int backlog = 1;
    if (listen(sock, backlog) != 0)
    {
        perror("listen");
        return 1;
    } else
    {
        printf("listen\n");
    }

    while(1)
    {
        int status;

        // accept socket
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        int connected_socks = accept(sock, (struct sockaddr *)&client, &len);

        if (connected_socks == -1)
        {
            perror("accept");
        } else
        {
            printf("accepted\n");
        }

        // recieve
        float input[SIZE*SIZE] = {};
        status = recv(connected_socks, input, sizeof(input), 0);

        if (status == -1)
        {
            perror("recv");
        } else
        {
            printf("received\n");
        }

        // print recieved data
        printf("[ ");
        for (size_t i=0; i<SIZE; ++i)
        {
            for (size_t j=0; j<SIZE; ++j)
            {
                printf("%f ", input[SIZE*i + j]);
            }
        }
        printf("]\n");

        // inference
        float output[SIZE] = {};
        inference(input, output);

        // send
        status = send(connected_socks, output, sizeof(output), 0);
        
        if (status == -1)
        {
            perror("send");
        } else 
        {
            printf("send\n");
        }

        close(connected_socks);
    }

    close(sock);
    return 0;
}

client.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 52579
#define SIZE 3


int main(void)
{
    int status;

    // create socket
    int sock = socket(AF_INET, SOCK_STREAM, 0);

    // struct about the connection destination
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = inet_addr("127.0.0.1");

    // connect server
    status = connect(sock, (struct sockaddr *)&server, sizeof(server));
    if (status == -1)
    {
        perror("connect");
    } else {
        printf("connected\n");
    }

    // input
    float input[SIZE*SIZE] = {
        0.f, 1.f, 2.f,
        3.f, 4.f, 5.f,
        6.f, 7.f, 8.f
    };

    // send
    status = send(sock, input, sizeof(input), 0);
    if (status == -1)
    {
        perror("send");
    } else {
        printf("send\n");
    }

    // recieve
    float output[SIZE] = {};
    status = recv(sock, output, sizeof(output), 0);
    if (status == -1)
    {
        perror("recv");
    } else {
        printf("received\n");
    }

    // print received data
    printf("[ ");
    for(size_t i=0; i<SIZE; ++i)
    {
        printf("%f ", output[i]);
    }
    printf("]\n");

    // close socket
    close(sock);

    return 0;
}

Ausführungsmethode

kompilieren


gcc -o server server.c
gcc -o client client.c

Lauf


$ ./server
listen
accepted
received
[ 0.000000 1.000000 2.000000 3.000000 4.000000 5.000000 6.000000 7.000000 8.000000 ]
send
./client
connected
send
received
[ 3.000000 12.000000 21.000000 ]

DL von C / C ++

TensorFlow verfügt über eine C / C ++ - API. Es ist etwas eigenartig, sollte aber nach dem Lesen der Dokumentation relativ einfach zu bedienen sein (obwohl es etwas schwierig zu kompilieren ist ...). Dies allein einzuführen wird eine beträchtliche Menge sein. Es gibt viele Artikel und Referenzquellcodes. Bitte beziehen Sie sich auf diese.

Bonus (Signal)

Wenn Sie das Programm beenden möchten, können Sie das Programm mit "Strg + C" beenden, wobei die Betriebssystemfunktion "Signal" verwendet wird. Sie können das Programm auch mit Strg + Z stoppen.

Wenn das Programm gestartet und die Initialisierung wie das Laden des Modells abgeschlossen ist, wird es gestoppt, indem ein Stoppsignal an sich selbst gesendet wird. Durch Senden eines Signals von einem externen Programm oder Befehl kann das Programm ohne Wartezeit neu gestartet und ausgeführt werden. Es ist eine sehr primitive Methode, aber manchmal ist es gut, wenn es sehr einfach und gut für Demonstrationszwecke ist.

Es ist nicht möglich, Daten direkt über die Kommunikation zu übertragen, aber Sie können sie beispielsweise direkt von der Kamera lesen und ausführen oder die Datei einfach lesen.

run_by_signal.c


#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>


int main(void)
{
    printf("Initialize...\n");
    // run some initialization here
    sleep(1);

    pid_t c_pid = getpid();
    printf("Send SIGCONT to PID: %d to run\n", c_pid);

    while(1)
    {
        raise(SIGTSTP);
        printf("run \n");
    }

    return 0;
}

send_signal.c


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>


int main(int argc, char *argv[]) {
    printf("send continue signal to PID:%s\n", argv[1]);
    kill(atoi(argv[1]), SIGCONT);
}

Ausführungsmethode

kompilieren


gcc -o run_by_signal run_by_signal.c
gcc -o send_signal send_signal.c

Lauf

Wenn run_by_signal ausgeführt wird, wird es initialisiert und sofort gestoppt. Danach wird durch Ausführen von "send_signal" "run_by_signal" neu gestartet, der Prozess ausgeführt (hier wird nur "run" ausgegeben) und sofort gestoppt.

Geben Sie bei der Ausführung von "send_signal" die Prozess-ID von "run_by_signal" an.

$ ./run_by_signal
Initialize...
Send SIGCONT to PID: 17088 to run

[1]+  Stopped                 ./run_by_signal
$ ./send_signal 17088
send continue signal to PID:17088
run

[1]+  Stopped                 ./run_by_signal

Sie können auch den Befehl kill verwenden, um dasselbe wie send_signal zu tun.

Lesen und verstehen Sie das Linux Programmer's Manual (Übersetzung) usw., um genaue Informationen zu diesem und dem endgültigen Signal zu erhalten. Ich finde das normal.

Zusammenfassung / Eindruck

Ich dachte: "Ich benutze nur Python, deshalb möchte ich ab und zu die Sprache C verwenden." Ich schrieb darüber, wie man die Zeit verkürzt, um das Ergebnis des Deep Learning-Modells zu erhalten, indem man Socket und Signal für die Kommunikation zwischen Prozessen verwendet und die Initialisierung im Voraus durchführt.

Ich habe Tensorflow mit C ++ - API verwendet, aber ich habe keine C-API, daher würde ich es gerne ausprobieren. Außerdem scheint Pytorch eine C ++ - API zu haben, daher würde ich es gerne bald versuchen. Es scheint der Python-Oberfläche ähnlich zu sein, daher habe ich von jemandem gehört, der daran gewöhnt ist, dass es nützlich ist.

Referenzenliste

Recommended Posts

Interprozesskommunikation für Embedded DL
Linux-Interprozesskommunikation
Arten der Kommunikation zwischen Prozessen
Kommunikation zwischen Prozessen ~ gemeinsamer Speicher ~