Communication interprocessus pour DL embarquée

Cet article est l'article du 20e jour du LeapMind Advent Calendar 2019.

emballer

J'écrirai sur la façon de raccourcir le temps pour obtenir le résultat du modèle Deep Learning en utilisant socket et signal pour la communication inter-processus en langage C et la pré-initialisation.

Auto-introduction

Je suis généralement un ** ingénieur Deep Learning basé sur Python **. Cette fois, j'utiliserai le ** langage C **. Il y a beaucoup d'ingénieurs qui utilisent C / C ++ chez LeapMind, qui développe la DL embarquée, mais je développe rarement moi-même en C / C ++. ** Je ne suis pas très compétent **, donc si vous avez des erreurs, faites-le moi savoir!

Objectif

Le titre est une communication inter-processus pour DL embarquée, mais c'est juste une communication inter-processus! En fait, je voulais exécuter mnist avec C / C ++, mais comme le montant est important, la partie inférence est factice (décrite plus tard).

Soit dit en passant, dans le cas des équipements embarqués dans l'industrie manufacturière, il y a des moments où l'on souhaite effectuer un traitement en peu de temps comme ** plusieurs dizaines de msec **. Si l'entrée est petite, même un modèle DL peut être capable de s'exécuter en dizaines de msec, Si vous chargez simplement le modèle DL créé par TensorFlow, le démarrage peut prendre ** plusieurs secondes **.

Dans un tel cas, en le lançant en tant que service à l'avance et en envoyant une demande, Vous pouvez éliminer le temps d'attente.

En fait, Python, qui est souvent utilisé en DL, a naturellement un module de socket, et il existe une bibliothèque qui peut être plus riche. Il y en a beaucoup, mais dans un environnement quasi embarqué, vous pouvez ou non vouloir installer Python. Par conséquent, vous devrez peut-être développer en ** C / C ++ etc. **.

environnement

L'environnement utilisé cette fois est le suivant.

veux dire

Il existe plusieurs façons de communiquer entre les processus. Cette fois, nous utiliserons la communication par socket pour la communication inter-processus. Aussi, en prime, j'aimerais écrire une méthode simple utilisant le signal.

Flux de processus

Le traitement de la communication du client et du serveur est comme indiqué dans la figure ci-dessous. En déduction, entrez une matrice 3x3 et calculez la somme pour chaque colonne. Une matrice 3x3 (9 colonnes float) est envoyée par le client pour que les 3 colonnes float communiquent.

Untitled Diagram.png

Code source

Il n'y a pas beaucoup de livres écrits sur les sockets, alors lisez les blogs techniques sur le net pour en avoir un aperçu, ** pour des informations précises, ** [** Linux Programmer's Manual (translation) **](https: / Je pense qu'il est normal de lire et de comprendre /linuxjm.osdn.jp/html/LDP_man-pages/man7/socket.7.html).

Articles / matériaux de référence

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;
}

Méthode d'exécution

compiler


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

Courir


$ ./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 par C / C ++

TensorFlow a une API C / C ++. C'est un peu bizarre, mais il devrait être relativement facile à utiliser après avoir lu la documentation (même si c'est un peu difficile à compiler ...). Introduire cela seul sera une quantité considérable. Il existe de nombreux articles et codes sources de référence, veuillez donc vous y référer.

Bonus (signal)

Lorsque vous voulez terminer le programme, vous pouvez terminer le programme avec ctrl + C, qui utilise la fonction OS appelée signal. Vous pouvez également arrêter le programme avec ctrl + Z.

Lorsque le programme est lancé et que l'initialisation telle que le chargement du modèle est terminée, il s'arrête en envoyant un signal d'arrêt à lui-même. En envoyant un signal depuis un programme ou une commande externe, le programme peut être redémarré et exécuté sans temps d'attente. C'est une méthode très primitive, mais parfois c'est bien quand c'est très simple et bon à des fins de démonstration.

Il n'est pas possible de transmettre des données directement par communication, mais par exemple, il est possible de lire directement depuis la caméra et de l'exécuter, ou de lire facilement un fichier.

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);
}

Méthode d'exécution

compiler


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

Courir

Lorsque run_by_signal est exécuté, il s'initialise et s'arrête immédiatement. Après cela, en exécutant send_signal, run_by_signal est redémarré, le processus est exécuté (ici, seul run est sorti), et il s'arrête immédiatement.

Lors de l'exécution de send_signal, spécifiez l'ID de processus de run_by_signal.

$ ./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

Vous pouvez également utiliser la commande kill pour faire la même chose que send_signal.

Lisez et comprenez le Manuel du programmeur Linux (traduction) etc. pour des informations précises sur ce signal et enfin. Je pense que c'est normal.

Résumé / impression

J'ai pensé: "Je n'utilise que Python, donc je veux utiliser le langage C de temps en temps." J'ai écrit sur la façon de raccourcir le temps pour obtenir le résultat du modèle Deep Learning en utilisant le socket et le signal pour la communication inter-processus et en effectuant l'initialisation à l'avance.

J'ai utilisé Tensorflow avec l'API C ++, mais je n'ai pas d'API C, alors j'aimerais l'essayer. De plus, Pytorch semble avoir une API C ++, j'aimerais donc l'essayer bientôt. Cela semble être similaire à l'interface Python, donc j'ai entendu de quelqu'un qui y était habitué que c'était utile.

Liste de références

Recommended Posts

Communication interprocessus pour DL embarquée
Communication interprocessus Linux
Types de communication inter-processus
Communication inter-processus ~ mémoire partagée ~