Cet article est l'article du 20e jour du LeapMind Advent Calendar 2019.
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.
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!
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. **.
L'environnement utilisé cette fois est le suivant.
Ubuntu==16.04
gcc==5.4.0
Cela devrait fonctionner sur une autre distribution ou Mac, et vous pouvez faire de même sous Windows avec winsock2.
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.
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.
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;
}
gcc -o server server.c
gcc -o client client.c
$ ./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 ]
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.
Dans Tensorflow, la mémoire est sécurisée dans la première exécution et la première opération est lente, donc en exécutant vide uniquement pour la première fois, il sera possible de s'exécuter sans temps d'attente après cela.
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);
}
gcc -o run_by_signal run_by_signal.c
gcc -o send_signal send_signal.c
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.
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.