IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Qt Discussion :

Problème de vitesse VBO et d'émission de signal [2D/3D]


Sujet :

Qt

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Alternant
    Inscrit en
    Avril 2021
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Alternant

    Informations forums :
    Inscription : Avril 2021
    Messages : 7
    Points : 5
    Points
    5
    Par défaut Problème de vitesse VBO et d'émission de signal
    Bonjour à tous,

    Je viens à votre rencontre en espérant que vous puissiez m'aider face à un problème de vitesse. Pour comprendre mon problème, il m'est important de vous expliquer simplement le contexte (sans lequel il sera difficile de me comprendre).

    J'ai mis en place un sniffer (capture de paquets) sur Qt (c++) qui a pour but de capturer environ 10000 paquets par seconde. Ces paquets sont mis dans une mémoire partagée accessible par un autre thread qui va permettre de décoder les paquets pour extraire ce que j'appelle des "cellules vidéos" (environ 1000 par paquet). Chaque cellule vidéo contient 4 données, que je dois transmettre à un autre thread afin de faire un traitement "pré-affichage". Je transmets donc par paquet, 4données * 1000 cellules à mon thread pré-affichage.

    Là est mon problème, dans ce thread pré-affichage, je dois calculer pour chaque cellules, 4 points (ce qui fait donc 8 coordonnées x,y), ainsi qu'une autre variable que l'on nommera Level. Toutes ses informations nouvellement calculées, sont envoyées à ma classe MyOpenGLWidget, dans une méthode updateVertices, qui va copier coller ma liste contenant toutes les coordonnées dans un VBO afin de réaliser un simple affichage plus tard.

    Le problème est que je dois capturer toutes les 1 secondes, environ 10 000 paquets (contenant chacun mes 1000 cellules), et que actuellement, la mise en place des coordonnées dans ma VBO est trop lente. Je copie toutes mes données seulement toutes les 5 secondes, alors que dans l'idéal, il faudrait que toutes les 2 secondes, mes données se retrouvent dans la VBO.

    Avez-vous des suggestions ? Je peux vous fournir le code si nécessaire.
    j'ai entendu parler des shaders, et fragment shaders, je me demandais même s'il n'était pas possible de mettre dans le VBO uniquement 1 point (donc 2 coordonnées) par cellule vidéo, afin de réaliser un shader qui calculera les 3 autres points de chaque cellule.


    Je vous remercie,
    si vous avez d'autres questions, n'hésitez pas

    Méthode dans laquelle je copie mes données dans le VBO
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void MyOpenGLWidget::updateVertices(const std::vector<double>& vertices)
    {       //Lecture du VBO + maj de ses données avec celles de allRadarCells
            tempVertices.insert(tempVertices.end(), vertices.begin(), vertices.end());
            if (tempVertices.size() >= 4096*2240*9)
            {
                glBindBuffer(GL_ARRAY_BUFFER, VBO);
                glBufferData(GL_ARRAY_BUFFER, tempVertices.size()*sizeof(double), tempVertices.data(), GL_DYNAMIC_DRAW);
                tempVertices.clear();
            }
    }
    Thread dans lequel je calcule les 8 coordonnées, que j'envoie vers ma méthode qui mettra les données dans le VBO :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    void vertexConversion::run()
    {
        while(!stopFlag)
        {
            QMutexLocker locker(&mutexvertex);
            while(radarCells.isEmpty()) {
                conditionvertex.wait(&mutexvertex);
            }
            auto cells = radarCells;
            radarCells.clear();
            locker.unlock();
            std::vector<double> vertices;
            for (const auto& cell : cells)
            {
                //qDebug()<<"Az_start:"<<cell.azimuth_start<<" Az_end:"<<cell.azimuth_end<<" Distance:"<<cell.distance;
                /*Conversion des coordonnées*/
                double X1_start = 640+((cell.distance)*cos(cell.azimuth_start))*64.0/550.0;
                double Y1_start = 300-((cell.distance)*sin(cell.azimuth_start))*64.0/550.0;
                double X2_start = 640+((cell.distance+2.5)*cos(cell.azimuth_start))*64.0/550.0;
                double Y2_start = 300-((cell.distance+2.5)*sin(cell.azimuth_start))*64.0/550.0;
     
                double X1_end = 640+((cell.distance)*cos(cell.azimuth_end))*64.0/550.0;
                double Y1_end = 300-((cell.distance)*sin(cell.azimuth_end))*64.0/550.0;
                double X2_end = 640+((cell.distance+2.5)*cos(cell.azimuth_end))*64.0/550.0;
                double Y2_end = 300-((cell.distance+2.5)*sin(cell.azimuth_end))*64.0/550.0;
     
                vertices.push_back(X1_start);
                vertices.push_back(Y1_start);
                vertices.push_back(X2_start);
                vertices.push_back(Y2_start);
     
                vertices.push_back(X1_end);
                vertices.push_back(Y1_end);
                vertices.push_back(X2_end);
                vertices.push_back(Y2_end);
     
                vertices.push_back(cell.signalLevel);
            }
     
            emit verticesReady(vertices);
        }
    }

  2. #2
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 584
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 584
    Points : 7 721
    Points
    7 721
    Par défaut
    Bonjour,

    Une chose qui peut faire des économies de CPU est d'éviter les recopies et les allocations. A minima, penser à les optimiser.

    Par exemple, avec juste 3 lignes adaptées dans le code du thread, ça devrait donner un gain visible:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    void vertexConversion::run()
    {
        while(!stopFlag)
        {
            QMutexLocker locker(&mutexvertex);
            while(radarCells.isEmpty()) {
                conditionvertex.wait(&mutexvertex);
            }
            auto cells = std::move(radarCells);          // transfert plutôt que copie car radarCells est vidée juste après.
            radarCells.clear();
            locker.unlock();
            std::vector<double> vertices;
            vertices.reserve( 9 * cells.size() );          // faire l'allocation mémoire en une seule fois
            for (const auto& cell : cells)
            {
                //qDebug()<<"Az_start:"<<cell.azimuth_start<<" Az_end:"<<cell.azimuth_end<<" Distance:"<<cell.distance;
                /*Conversion des coordonnées*/
                double X1_start = 640+((cell.distance)*cos(cell.azimuth_start))*64.0/550.0;
                double Y1_start = 300-((cell.distance)*sin(cell.azimuth_start))*64.0/550.0;
                double X2_start = 640+((cell.distance+2.5)*cos(cell.azimuth_start))*64.0/550.0;
                double Y2_start = 300-((cell.distance+2.5)*sin(cell.azimuth_start))*64.0/550.0;
     
                double X1_end = 640+((cell.distance)*cos(cell.azimuth_end))*64.0/550.0;
                double Y1_end = 300-((cell.distance)*sin(cell.azimuth_end))*64.0/550.0;
                double X2_end = 640+((cell.distance+2.5)*cos(cell.azimuth_end))*64.0/550.0;
                double Y2_end = 300-((cell.distance+2.5)*sin(cell.azimuth_end))*64.0/550.0;
     
                vertices.push_back(X1_start);
                vertices.push_back(Y1_start);
                vertices.push_back(X2_start);
                vertices.push_back(Y2_start);
     
                vertices.push_back(X1_end);
                vertices.push_back(Y1_end);
                vertices.push_back(X2_end);
                vertices.push_back(Y2_end);
     
                vertices.push_back(cell.signalLevel);
            }
     
            emit verticesReady( std::move(vertices) );   // encore un transfert (et verticesReady() doit s'attendre à recevoir une value ou une x-référence pas une référence)
        }
    }

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Alternant
    Inscrit en
    Avril 2021
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Alternant

    Informations forums :
    Inscription : Avril 2021
    Messages : 7
    Points : 5
    Points
    5
    Par défaut Tests
    Bonsoir,

    Je vous remercie pour cette réponse.

    Je ne connaissais pas cette fonction (move), je vais essayer d’optimiser cela demain dès que possible et je vous ferais un retour. En revanche je ne suis pas sûr que cela résoudra les problèmes car j’ai pu constater que les calculs au sein de la boucle for sont aussi ralentissants.

    J’ai entendu parler du parallélisme (multi thread via l’utilisation de la syntaxe #pragma ?). J’ai essayé aujourd’hui de mettre en place cette idée pour gérer ma boucle for, mais je me suis retrouvé avec des erreurs.

    Si d’ailleurs vous avez une idée de comment le mettre en place, je suis preneur !

    Je vous remercie,
    Bonne soirée

  4. #4
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 902
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 902
    Points : 219 982
    Points
    219 982
    Billets dans le blog
    126
    Par défaut
    Bonjour,

    Je suis d'accord avec le reserve(). Mais je pense qu'il y a mieux à faire. Bien sûr, qui dit problème de performance dit: profiling. Chaque "optimisation" doit être validée par du profiling.
    Si c'est un problème avec les VBO (envoi des données au GPU), il est possible de passer par glMapBuffer() qui permet de mapper la mémoire GPU du côté du CPU et donc de faire un copie directement dans celle-ci. Au minimum, cela vous enlève un std::vectorPour le parallélisme, si vous parlez de pragma, c'est sûrement du OpenMP. C'est quelque fois facile à mettre en place, mais pas toujours .
    En C++ moderne (C++17/C++20), on a du parallélisme pour presque rien. Je pense aux std::transform ( https://en.cppreference.com/w/cpp/algorithm/transform ) ou encore, std::for_each ( https://en.cppreference.com/w/cpp/algorithm/for_each ), notez le ExecutionPolicy. Attention toutefois à l'accès en écriture à vos vecteurs, car cela demande des précautions (mutex, mais les mutex sont (autant que possible) à éviter afin d'éviter que les threads ne se gênent entre eux). Le reserve devrait bien aider du coup .
    Pas certain que cela vous aide, mais cet article est assez intéressant : https://www.cppstories.com/2023/spans-threads-cpp20/

    Pour les shaders, cela dépend de si vous pouvez transposer les calculs sur le GPU ou non. Sans que l'on sache ce que vous faites réellement de vos données, dur à dire.

  5. #5
    Futur Membre du Club
    Homme Profil pro
    Alternant
    Inscrit en
    Avril 2021
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Alternant

    Informations forums :
    Inscription : Avril 2021
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Bonjour,

    Merci pour toutes les pistes !

    Tout d'abord, laissez moi vous partager les dernières nouvelles :
    -utilisation de std::move => Pas de changements (en terme de performance) significatifs. Le problème, étant lié au nombre de calculs présents dans la boucle for, m'a amené à revoir la structure de mon programme. En particulier la manière dont les données sont transmises d'un thread à un autre.
    - Remplacement des émissions de signal et des mutex permettant le transfert des données inter-threads, par des files producteur/consomateur => Pas de changements significatifs. (Voici un lien github de l'implémentation que je vous recommande malgré tout : https://github.com/cameron314/concurrentqueue).

    Actuellement dans le programme, je ne place plus mes données dans le VBO et j'observe toujours un délai de 5 secondes au total pour traiter les 4096(azimuts)*2240(cellules) et calculer leurs 4 points (8 coordonnées) respectifs. J'ai donc redéfini mon problème : Les échanges inter-thread ne sont pas assez rapides.
    Le manque d'expérience en c++ doit en être la raison.

    Concernant votre message LittleWhite, tout d'abord un grand merci pour ces nouvelles pistes !
    Je vais essayer de me renseigner sur comment faire du profiling sur Qt, et surtout comment l'interpréter. Après effectivement je devrais utiliser glMapBuffer, je ne savais pas qu'on pouvait obtenir un pointeur direct à la mémoire du GPU. Cependant vu qu'il s'agit de calculs que je réalise dans un thread de "pré-traitement" séparé de mon thread "traitement" héritant d'un widget OpenGL, est-ce que je peux réaliser la copie depuis ici ?
    Pour le parallélisme effectivement je parlais de OpenMP. Je vais prendre le temps d'étudier ce nouveau lien, j'ai vu des choses qui pourraient effectivement m'intéresser .

    En revanche maintenant, pour corriger mon problème il faudrait que je vous partage le code pour que vous puissiez en tiré une conclusion selon votre expérience professionnelle.
    Alors voici le fichier packetprocess.cpp, qui a pour rôle d'exporter d'un paquet, récupéré d'une mémoire partagée, les données nous intéressants (2 azimuts, 1 distance calculée, 1 niveau de signal) :
    PS : Données exportées de packetprocess.cpp vers vertexConversion.cpp (déjà partagé).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    #include "packetprocess.h"
    #include "packet_struct.h"
    #include "QDebug"
    #include<QtMath>
    #include <QDateTime>
    #include <netinet/in.h>
     
    moodycamel::ConcurrentQueue<QVector<RadarCell>> packetprocess::cellQueue;
     
    packetprocess::packetprocess(QObject* parent) : QThread(parent)
    {}
    void packetprocess::addPacket(const QByteArray& packet)
    {
        packetQueue.enqueue(packet);
    }
     
    /*Si arrêt du programme = transmission des dernières coordonnées stockées*/
    void packetprocess::stopProcess()
    {
        stopFlag = true;
        if (!tempCells.isEmpty())
        {
            cellQueue.enqueue(std::move(tempCells));
            tempCells.clear();
        }
    }
     
    Filter *packetprocess::getFilterAddress() {                          //Définition de la fonction getFilterAddress pour obtenir l'adresse de l'objet m_filter
        return &m_filter;                                        //Renvoyer un pointeur vers l'objet Filter pour configurer les filtres
    }
     
    /*Décode les paquet, et transmet lorsque nécessaire les données : distance, az_start, az_end, signalLVL au thread vertexConversion*/
    void packetprocess::decodeAsterixHeader(const QByteArray& packet)
    {
        /*Initialisation*/
        const Asterix240* asterixHeader = reinterpret_cast<const Asterix240*>(packet.data() + sizeof(MacHeader) + sizeof (IpHeader));
        double a_start,a_end,dist;
     
        /*Récupération de l'azimut + start_range + cell_duration*/
        uint16_t start_az_raw = (static_cast<uint16_t>(asterixHeader->video_header_nano[0]) <<8) | asterixHeader->video_header_nano[1];
        uint32_t start_rg_raw = (asterixHeader->video_header_nano[4] << 24) | (asterixHeader->video_header_nano[5] << 16) | (asterixHeader->video_header_nano[6] << 8) | asterixHeader->video_header_nano[7];
        uint16_t end_az_raw = (static_cast<uint16_t>(asterixHeader->video_header_nano[2])<<8) | asterixHeader->video_header_nano[3];
     
        /*Transmission des dernières coordonnées x;y et de signal
         *stockés si détection d'un nouveau azimut*/
        if (start_az_raw != lastAzimuth && !tempCells.isEmpty())
        {
            cellQueue.enqueue(std::move(tempCells));
            tempCells.clear();
        }
     
        lastAzimuth = start_az_raw;
        a_start = ((start_az_raw)/2)*M_PI/180.0;
        a_end = ((end_az_raw)/2)*M_PI/180.0;
     
        /*Boucle for permettant d'ajouter pour toutes les cellules du paquet en cours : distance, az_start, az_end, signalLVL
         * à tempCells*/
        for (int i=0;i<(asterixHeader->size_of_video_data)*64;i++)
        {
            dist = (cell_dur * (start_rg_raw+i+1-1)*c/2.0);
            tempCells.append(RadarCell(a_start,a_end,dist,static_cast<double>(asterixHeader->video_block_medium_data_volume[i])));
        }
     
        /*Boucle if détectant si le nouveau paquet traité est un paquet de "fin" d'azimut, si oui = transmission des cellules
         * tempCells dans cellQueue.*/
        if (asterixHeader->size_of_video_data == 15)
        {
            cellQueue.enqueue(std::move(tempCells));
            tempCells.clear();
        }
    }
     
    void packetprocess::run()
    {
        while(!stopFlag) {
            QByteArray packet;
            {
               while (packetQueue.try_dequeue(packet))
               {
                   /*Traitemnt de sniff::run*/
                    m_filter.inputData(packet.data());//Remplissage du databuf de filter permettant les filtrages
     
                    if(!m_filter.isIP()) {
                    continue;}
                    if(!m_filter.isAllowed()) {
                    //std::cout <<"PAQUET SAUTÉ" << std::endl; //Test du filtrage des paquets
                    continue;}
     
                    decodeAsterixHeader(packet); //Fonction permettant l'extraction des données voulues
               }
            }
     
        }
    }
    J'ai vu votre portfolio LittleWhite, est-il possible de vous contacter pour échanger plus de détails si le temps vous le permet ?

    Et finalement pour les shaders, je pense que j'en aurais besoin. Je suis censé pouvoir générer des formes à partir des coordonnées calculées pour chaque cellule, et d'associer à chaque forme un niveau de "couleur" (=signal). Ce qui me permettra d'afficher une image radar .

  6. #6
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 902
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 902
    Points : 219 982
    Points
    219 982
    Billets dans le blog
    126
    Par défaut
    Pour le profiling, dans Qt Creator, c'est dans le menu "Analyser" et "Analyseur de performance". Je n'utilise généralement pas l'intégration dans Qt Creator et pour la visualisation, j'utilise maintenant hotspot. Bref, sous Linux, c'est avec perf que l'on fait du profiling (en release, avec les symboles de débogage). Sous Windows, j'utilise Visual Studio .

    Je n'aurai pas utiliser une bibliothèque pour la concurrence. J'ai l'impression que vous devriez travailler sur un segment de mémoire partagé (mmap sous Linux).
    Plusieurs approches sont possibles :
    • faire les choses, de manière multithread, mais étape par étape (synchronisation facile) ;
    • faire les choses en parallèle (certains threads dédiés à la première étape, certain dédiés à le seconde étape) ;

    Mais il est certains qu'il faille éviter tous les points de synchronisation.

    Le glMapBuffer doit être utilisé lorsque vous êtes prêt à envoyer des données au GPU. Car vous allez sûrement avoir besoin du tampon pour faire le rendu et vous ne voulez pas bloquer le rendu . Mais du coup, on pourra imaginer un système avec plusieurs tampon (un pour l'affichage, un pour le travail). La copie, peut être réalisée de n'importe où.
    Mais comme vous avez trouvé que le problème n'est pas côté OpenGL, alors ne vous préoccupez pas de cela pour le moment

  7. #7
    Futur Membre du Club
    Homme Profil pro
    Alternant
    Inscrit en
    Avril 2021
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Alternant

    Informations forums :
    Inscription : Avril 2021
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Bonjour,

    Je suis en train d'adapter le programme pour mieux gérer la transition des données inter-thread comme prévu .

    J'aime l'idée de la mémoire partagée ! Cependant je ne peux pas m'en servir car lorsque packetprocess.cpp fera son traitement et sera donc en "lecture" de la mémoire partagée, sniff.cpp ne pourra pas écrire dessus. Et si je met en place une synchronisation entre le thread de capture et le thread de décodage des paquets, sniff.cpp ne pourra pas capturer les paquets en cours car il ne pourra pas les écrire, ce qui causera le saut de paquets. Car au final le débit des paquets est élevé (Environ 8000 à 10000 paquets/s).

    Du coup peut-être qu'un buffer circulaire serait plus approprié pour dans un premier temps le transfert des paquets, puis une mémoire partagée pour le transfert des variables de traitement ?

  8. #8
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 902
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 902
    Points : 219 982
    Points
    219 982
    Billets dans le blog
    126
    Par défaut
    En effet, je pensais à un tampon circulaire.
    L'idée est bien entendu d'éviter que le thread de sniff bloque le thread de la tâche suivante, et inversement. J'imagine que plusieurs solutions sont possibles, mais comme ça, à chaud, on pourrait imaginer une tête de lecture et une tête d'écriture. Le thread de sniff incrémente la tête d'écriture pour chaque paquet inscrit, le thread de la tâche suivante incrémente la tête de lecture et compare avec la tête d'écriture pour s'arrêter (et attendre que la condition lecture == ecriture ne soit plus vraie). Pas besoin de mutex sur la tête d'écriture, le thread peut la lire autant qu'il veut et même si elle est non à jour, c'est pas très grave. Par contre, la condition, c'est certainement une variable de condition que l'on peut notifier (ou une attente active ).

  9. #9
    Futur Membre du Club
    Homme Profil pro
    Alternant
    Inscrit en
    Avril 2021
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Alternant

    Informations forums :
    Inscription : Avril 2021
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Problème résolu, je vous conseil à tous de vous tourner vers des buffers circulaires

    Merci pour votre aide !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [SAX] Vitesse contre place en mémoire
    Par Dinaïz dans le forum Format d'échange (XML, JSON...)
    Réponses: 6
    Dernier message: 15/10/2004, 13h37
  2. Vitesse du CPU, quantité de RAM... en C
    Par dclink dans le forum C
    Réponses: 4
    Dernier message: 07/07/2003, 20h48
  3. Comment repérer la vitesse du processeur?
    Par Paradam dans le forum Assembleur
    Réponses: 14
    Dernier message: 28/06/2003, 10h43
  4. Vitesse de compilation
    Par srvremi dans le forum C++Builder
    Réponses: 5
    Dernier message: 30/07/2002, 16h49
  5. Vitesse de la mémoire vidéo
    Par Anonymous dans le forum x86 16-bits
    Réponses: 3
    Dernier message: 06/06/2002, 20h20

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo