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

VB.NET Discussion :

Optimisation de téléchargement/écriture de fichiers


Sujet :

VB.NET

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 91
    Par défaut Optimisation de téléchargement/écriture de fichiers
    Bonsoir,

    J'ai créé une application qui télécharge sur plusieurs threads/sockets et qui écrit a la fois dans un fichier.

    Pour l'instant voici ce que je fais :

    Tout ce qui suit est dans un backgroundworker et les index etc.. sont géré par une bdd sqlce

    Téléchargement asynchrone avec BeginReceive

    à chaque fois que ma callback est appelée j' "Invoke" un délégué qui va écrire ce qui a été téléchargé, donc ~BUFFER_SIZE dans un fichier, pour l'instant mon BUFFER_SIZE est de 3500 octets.

    Ca veut dire que a chaque fois qu'un paquet est reçu je lui intime l'ordre d'écrire directement mes 3500 octet dans un fichier.

    [mode j'aime bien emmètre des doutes sur des choses évidentes]
    Je pense qu'il faut que je limite un maximum mon nombre de traitement dans mon callBack déjà, pensez vous que l'appel à Invoke surchage ma callback ? a priori non ?
    [/eoj]

    Est-ce que je ne devrais pas plutôt créer mon propre buffer qui ferait par exemple 10Mo et attendre que celui ci soit rempli pour lancer l'écriture du fichier ?

    Le soucis si je fais ca c'est que je risque d'utiliser beaucoup de ram, et si je met par exemple un block_size de 10Mo, ca peut faire super lourd si je met 10 threads par exemple

    Enfin voilà c'est pas évident d'arriver à optimiser

    J'aurais voulu savoir également pourquoi lorsque je me fais par exemple un tableau de byte de taille 4000 octet, et que je lui dit de me télécharger 4000 octet à l'appel de beginreceive mon tableau est trop petit ?

    Quelques bout de mon code :

    Callback de téléchargement :
    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
        Public Sub callB(ByVal ar As IAsyncResult)
            Dim current As DBChunk = CType(ar.AsyncState, DBChunk)
            Dim read As Integer = _socket.EndReceive(ar)
            If (read = 0) Then
                _socket.Close()
            Else
                Dim index As Integer = 0
                If (current.current_byte = current.start_byte) Then
                    Dim s As String = Encoding.ASCII.GetString(_buffer, 0, read)
                    If (s.Contains("HTTP/")) Then
                        index += s.IndexOf(vbCrLf & vbCrLf) + 4
                    Else
                        MsgBox("ERREUR, pas de GET dans le paquet c'est CHELOU")
                    End If
                End If
     
                Dim de As New DataDownloadEvent(_buffer, read - index)
                de.CurrentFirstWriteIndex = index
     
                Dim r As New received(AddressOf _form.dlm._dm.worker2)
                de.CurrentChunk = current
                _form.Invoke(r, New Object() {Me, de})
                current.current_byte += read - index
                _socket.BeginReceive(_buffer, 0, READ_SIZE, SocketFlags.None, AddressOf callB, current)
            End If
        End Sub
    Ecriture du fichier :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
        Public Sub worker2(ByVal sender As Object, ByVal e As DataDownloadEvent)
            fs.Seek(e.CurrentChunk.current_byte, SeekOrigin.Begin) 
            fs.Write(e.Received, e.CurrentFirstWriteIndex, e.ByteReceived)
            fs.Flush()
            RaiseEvent DataReceived(Me, New DataDownloadEvent(e.ByteReceived))
        End Sub
    Je suis ouvert à toute remarque

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Euh... pourquoi tu fais un Invoke ?

    Invoke, ça sert à exécuter du code sur le thread de l'interface graphique, parce que les autres threads n'ont pas le droit de toucher à l'interface graphique. Mais ta méthode worker2 n'a rien à voir avec ça, elle ne fait qu'écrire dans un fichier...

    Le seul résultat de ton Invoke, c'est que tu fais plein de boulot dans le thread de l'UI, ce qui réduit la réactivité de l'interface.

    Si tu fais ça pour être sûr qu'un seul thread à la fois écrit dans le fichier, il y a d'autres moyens plus adaptés de le faire. Le plus simple est d'utiliser l'instruction SyncLock :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        Private ReadOnly _syncObject As New Object()
     
        Public Sub worker2(ByVal sender As Object, ByVal e As DataDownloadEvent)
            SyncLock _syncObj
                fs.Seek(e.CurrentChunk.current_byte, SeekOrigin.Begin) 
                fs.Write(e.Received, e.CurrentFirstWriteIndex, e.ByteReceived)
                fs.Flush()
            End SyncLock
            RaiseEvent DataReceived(Me, New DataDownloadEvent(e.ByteReceived))
        End Sub
    Le premier thread qui entre dans le bloc SyncLock acquiert un verrou sur l'objet _syncObject. Si un autre thread essaie de rentrer dans ce bloc, il restera bloqué jusqu'à ce que le premier en soit sorti. De cette façon, un seul thread peut exécuter ce code à un moment donné.

    D'une manière générale, évite d'utiliser Invoke pour faire des traitements lourds : utilise le seulement si tu dois accéder à l'interface graphique

  3. #3
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 91
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Euh... pourquoi tu fais un Invoke ?

    Invoke, ça sert à exécuter du code sur le thread de l'interface graphique, parce que les autres threads n'ont pas le droit de toucher à l'interface graphique. Mais ta méthode worker2 n'a rien à voir avec ça, elle ne fait qu'écrire dans un fichier...

    Le seul résultat de ton Invoke, c'est que tu fais plein de boulot dans le thread de l'UI, ce qui réduit la réactivité de l'interface.

    Si tu fais ça pour être sûr qu'un seul thread à la fois écrit dans le fichier, il y a d'autres moyens plus adaptés de le faire. Le plus simple est d'utiliser l'instruction SyncLock :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        Private ReadOnly _syncObject As New Object()
     
        Public Sub worker2(ByVal sender As Object, ByVal e As DataDownloadEvent)
            SyncLock _syncObj
                fs.Seek(e.CurrentChunk.current_byte, SeekOrigin.Begin) 
                fs.Write(e.Received, e.CurrentFirstWriteIndex, e.ByteReceived)
                fs.Flush()
            End SyncLock
            RaiseEvent DataReceived(Me, New DataDownloadEvent(e.ByteReceived))
        End Sub
    Le premier thread qui entre dans le bloc SyncLock acquiert un verrou sur l'objet _syncObject. Si un autre thread essaie de rentrer dans ce bloc, il restera bloqué jusqu'à ce que le premier en soit sorti. De cette façon, un seul thread peut exécuter ce code à un moment donné.

    D'une manière générale, évite d'utiliser Invoke pour faire des traitements lourds : utilise le seulement si tu dois accéder à l'interface graphique
    Enfaite, je sais bien que c'est censé freezer l'UI, mais j'ai pas trouvé de méthode Invoke dans les thread par exemple, ca parait être du bricolage total ce que je veux faire, mais pas du tout, enfaite je voudrais que tout soit fait de façon asynchrone pour que mon écriture de fichier plombe pas mon téléchargement (c'est un accélérateur de téléchargement que je fais).


    Donc tu va surement pouvoir m'aider enfaite, je voudrais enfaite lancer X thread au final et j'ai pensé que faire un singleton serait pas mal pour mon DataManager ( la classe qui s'occupe des écriture de fichier ), Enfaite ce que je voudrais c'est :

    Download :2~ beaucoup de thread
    écriture de fichier : 1 thread - 1 instance
    UI : (1 thread)

    Download ---- On sépare bien ici/Pile d'évènements -----> écriture de fichier -----> UI/stats

    Au final je veux hiérarchiser les priorités, je veux pas avoir de lock, je pourrais mais c'est inutile étant donné que là je vais utiliser une pile d'évènement (j'ai déjà utilisé les lock sur d'autres applis, c'est pas que je sais pas les utiliser).

    J'espère que tu vois ce que je veux dire

    Enfaite, j'avais peur dans me lancer dans un système ou un thread créer un autre qui créer un autre ( a noté que j'utilise les backgroundworker pour aller plus vite ), parce que plus j'appel un thread dans un autre, a la fin j'avais peur de perdre le contrôle sur mon UI
    tu crois que je devrais utiliser des thread purs pour faire ce que je veux faire ?


    A noté que j'ai essayé d'utiliser BeginWrite mais c'est un enfer parce que j'appel seek avant et ca déconne, pour un fichier de 19Mo ca me télécharge 246 Mo (alors que sans le fichier est le clone de celui sur le serveur), du coup je vois pas trop comment seek asynchrone

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par anat1212 Voir le message
    je voudrais que tout soit fait de façon asynchrone pour que mon écriture de fichier plombe pas mon téléchargement
    Euh... à moins que tu aies une connexion monstrueusement rapide et/ou un disque extrêmement lent, ce risque est à peu près inexistant...

    De toutes façons, même si le risque existait, tu te retrouverais face à un autre problème : les données téléchargées et pas encore écrites sur le disque doivent être stockées en mémoire en attendant, et la RAM n'étant pas une ressource illimitée, ça se terminerait par une OutOfMemoryException...

    Citation Envoyé par anat1212 Voir le message
    Enfaite, j'avais peur dans me lancer dans un système ou un thread créer un autre qui créer un autre ( a noté que j'utilise les backgroundworker pour aller plus vite ), parce que plus j'appel un thread dans un autre, a la fin j'avais peur de perdre le contrôle sur mon UI
    tu crois que je devrais utiliser des thread purs pour faire ce que je veux faire ?
    Thread ou BackgroundWorker, ça change pas grand chose au final... Le principal avantage du BGW est de déclencher les évènements sur le thread de l'UI, mais tu peux gérer ça manuellement si besoin. Pour ce qui est de créer un thread à partir d'un autre, tu n'as pas besoin de faire ça de toutes façons...

    A mon avis tu te compliques la vie... Plutôt que de réinventer la roue, utilise simplement WebClient.DownloadFileAsync, ça fait très bien le boulot. Je doute que tu arrives à faire quelque chose de plus rapide avec ta technique.

  5. #5
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 91
    Par défaut
    Je ne ferais pas tout ca si je pourrais l'écrire en une fonction

    Je sais bien que mon disque est plus rapide que mes connexions, mais mon appli fait énormément bouger la tête d'écriture du disque dur (j'ai pas de SSD).

    Par exemple dans un fichier de 10 000 000 octet avec 2 thread je vais me mettre à écrire les offset suivants :
    0-4 ko 1023-1027ko 5-8ko 1028-1032
    et ce a une vitesse très rapide, donc vu que j'arrive a avoir 1.4mo/sec (pour l'instant)
    ca fait en gros 350 écriture sur le disque par seconde, je veux pas faire l'idiot mais vu comment en conçu mon logiciel je pense que j'arrive à une limite, et comme tu dis c'est pas non plus une bonne idée de blinder la ram.

    Pourquoi j'utilise pas un code tel que Webclient DownloadFile ou autre ?

    Tout simplement parce que aucune de ces méthode ne fait ce que je recherche, je veux pouvoir binder ma socket sur tel ou tel interface, j'utilise le header Range de HTTP pour faire mon logiciel.

    L'idée est de pouvoir télécharger a partir de N interface internet, pour l'instant j'ai un voisin qui me permet l'accès a sa connexion, j'arriverais donc à 2Mo/seconde si mon logiciel est optimisé.

    Enfaite voici un thread que j'ai créé :
    http://www.developpez.net/forums/d99...-developpeurs/

    Je pense qu'au final je vais avoir
    RANGE_SIZE qui correspond à la taille des bouts à couper ( http range )
    FILE_BUFFER_SIZE qui correspond à la taille ( buffer de téléchargement )
    RECV_BUFFER_SIZE qui correspond au nombre d'octet à lire a chaque fois sur la socket

    Qu'est-ce que tu me conseil comme taille de buffer réseau ?

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par anat1212 Voir le message
    Qu'est-ce que tu me conseil comme taille de buffer réseau ?
    Aucune idée...

Discussions similaires

  1. [XL-2010] Optimiser le téléchargement de fichiers .csv sur internet via VBA
    Par funtim78 dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 08/01/2013, 09h18
  2. Réponses: 16
    Dernier message: 28/02/2011, 22h10
  3. Optimiser l'affichage d'un fichier XML de grosse taille...
    Par UnPeuPerdu dans le forum XML/XSL et SOAP
    Réponses: 11
    Dernier message: 03/06/2004, 16h01
  4. [langage] Optimiser la lecture d'un fichier
    Par And_the_problem_is dans le forum Langage
    Réponses: 4
    Dernier message: 05/02/2003, 08h54
  5. [langage] Optimiser la lecture d'un fichier
    Par And_the_problem_is dans le forum Langage
    Réponses: 2
    Dernier message: 11/06/2002, 10h24

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