Accélérez un code

MiniApp Messages postés 654 Date d'inscription lundi 21 juillet 2014 Statut Membre Dernière intervention 22 février 2019 - Modifié par MiniApp le 30/07/2014 à 13:51
MiniApp Messages postés 654 Date d'inscription lundi 21 juillet 2014 Statut Membre Dernière intervention 22 février 2019 - 4 août 2014 à 14:57
Bonjour,

Je souhaiterai accélérez ce code consistant à trouver les IPv4 dispo.

procedure TForm2.BitBtn1Click(Sender: TObject);
Var
Byte1:Byte;
Byte2:Byte;
Byte3:Byte;
Byte4:Byte;
begin
ShowMessage('Le sereur à 100ms pour répondre');
ListBox1.Items.Add('---Liste des IP positif---');
For Byte1 := 0 To 255 do
  Begin
  ProgressBarByte1.Position := Byte1;
  PanelByte1.Caption := 'Valeur actuel : '+IntToStr(Byte1);
    For Byte2 := 0 To 255 do
      Begin
      ProgressBarByte2.Position := Byte2;
      PanelByte2.Caption := 'Valeur actuel : '+IntToStr(Byte2);
        For Byte3 := 0 To 255 do
          Begin
          ProgressBarByte3.Position := Byte3;
          PanelByte3.Caption := 'Valeur actuel : '+IntToStr(Byte3);
            For Byte4 := 0 To 255 do
              Begin
              ProgressBarByte4.Position := Byte4;
              PanelByte4.Caption := 'Valeur actuel : '+IntToStr(Byte4);
                //IPv4
              PanelIP.Caption := 'IP actuel ' + IntToStr(Byte1)+'.'+IntToStr(Byte2)+'.'+IntToStr(Byte3)+'.'+IntToStr(Byte4);
              If Pos('[',Form1.RunDosInMemo('Ping -n 1 -w 100 -4 '+IntToStr(Byte1)+'.'+IntToStr(Byte2)+'.'+IntToStr(Byte3)+'.'+IntToStr(Byte4)).Text) <> 0 Then
                ListBox1.Items.Add(IntToStr(Byte1)+'.'+IntToStr(Byte2)+'.'+IntToStr(Byte3)+'.'+IntToStr(Byte4));
              end;
          end;
      end;
  end;
end;


Le code RunDosInMemo (trouver ici -> http://codes-sources.commentcamarche.net/forum/affich-10032748-faire-un-ping-en-delphi#./affich-10032748-faire-un-ping-en-delphi?&_suid=140672017703309068436680718062 ) :
{ code original de Zarko Gajic:
  http://delphi.about.com/cs/adptips2001/a/bltip0201_2.htm
  Modifié par Cirec (sur CodesSources [comment ça marche ?]) et par MiniApp}
Function TForm1.RunDosInMemo(const DosApp: string):TStringList;
const
  ReadBuffer = 2400;
var
  Security: TSecurityAttributes;
  ReadPipe, WritePipe: THandle;
  start: TStartUpInfo;
  ProcessInfo: TProcessInformation;
  Buffer: Pchar;
  BytesRead: DWord;
begin
Result := TStringList.Create;
  with Security do
  begin
    nlength := SizeOf(TSecurityAttributes);
    binherithandle := true;
    lpsecuritydescriptor := nil;
  end;
  if Createpipe(ReadPipe, WritePipe, @Security, 0) then
  try
    FillChar(Start, Sizeof(Start), #0);
    start.cb := SizeOf(start);
    start.hStdOutput := WritePipe;
    start.hStdInput := ReadPipe;
    start.dwFlags := STARTF_USESTDHANDLES +
      STARTF_USESHOWWINDOW;
    start.wShowWindow := SW_HIDE;

    if CreateProcess(nil, PChar(DosApp), @Security, @Security, true,
         NORMAL_PRIORITY_CLASS, nil, nil, start, ProcessInfo) then
    try
      repeat
        Application.ProcessMessages;
      until (WaitForSingleObject(ProcessInfo.hProcess, 100) <> WAIT_TIMEOUT);
      Buffer := AllocMem(ReadBuffer + 1);
      try
        repeat
          BytesRead := 0;
          ReadFile(ReadPipe, Buffer[0], ReadBuffer, BytesRead, nil);
          Buffer[BytesRead] := #0;
          OemToAnsi(Buffer, Buffer);
          Result.Text := Result.text + string(Buffer);
        until (BytesRead < ReadBuffer);
      finally
        FreeMem(Buffer);
      end;
    finally
      CloseHandle(ProcessInfo.hProcess);
      CloseHandle(ProcessInfo.hThread);
    end;
  finally
    CloseHandle(ReadPipe);
    CloseHandle(WritePipe);
  end;
end;


Ce code est très lent (il fait 5 IP secondes hors comme j'ai 255^4=4 228 250 625 IP à tester ce qui fait 845 650 125 secondes pour le code donc 587 257,031 25 heures)
Je veux bien utiliser des threads , mais je ne sais pas comment ça se fait merci d'avance.


Chercher et essayer : vous trouverez la solution!
Fouiner et regarder partout : vous trouverez la connaissance!!

7 réponses

Francky23012301 Messages postés 400 Date d'inscription samedi 6 août 2005 Statut Membre Dernière intervention 11 février 2016 1
Modifié par Francky23012301 le 31/07/2014 à 21:51
Tu es tétue ;) .

- ton code n'est absolument pas optimisé et sur plusieurs points : Par exemple dans ton code tu ne détruit pas à la fin de la procédure les objets que tu construits (TStringList).

-Ensuite quand je parle de formatage je parlais de la fonction format : IntToStr c'est has been surtout que tu fais de la concaténation en sus.

-Ton TempsList.Capacity n'apporte rien ;).

-Créer 256^4 (et non 255^4 puisque tu vas de 0 à 255) threads est une utopie de débutant : essaye et tu vas voir ce qui va se passer hu hu hu.

Regarde ce lien http://blogs.technet.com/b/markrussinovich/archive/2009/07/08/3261309.aspx et tu vas vite comprendre l'absurdité de la chose et tu verras en prime que ton code risque de ne pas etre portable sur un autre pc.

Pour finir et surtout c'est l'essentiel : qu'est ce que tu veux faire exactement ? Explique nous ton objectif car il est évident que ton idée est mal ficelée et est donc vouée à l'échec et peut etre que l'on peut mieux t'aiguiller.

Tu es débutant (Ca se voit ;) ) : Accepte les conseils que l'on te donne :). Si quelqu'un qui a une certaine expérience et une certaine expertise te dit que ton code n'est pas optimisé et que ton projet est mal ficelé c'est que c'est le cas ;). On est là pour aider les gens pas pour les dires des bétises ;)
1
Francky23012301 Messages postés 400 Date d'inscription samedi 6 août 2005 Statut Membre Dernière intervention 11 février 2016 1
30 juil. 2014 à 14:59
Ton source est lent pour plusieurs raisons :

1)D'abord un ping comme tu le fais, c'est à dire avec un round-trip time, prend inévitablement du temps et tu ne pourras rien y faire. De ce fait ton histoire est mal barré, même en utilisant des threads cela prendra un temps considérable.

2)Ensuite au niveau de ton code il y a des erreurs de conception : Tu fais 4 boucles imbriquées et en plus à chaque étape tu affiches une requete dans un composant visuel (listbox ou panel ou progressbar) induisant un source qui à la fin n'est absolument pas obtimisé et des risques de freezage à un moment. Moi à ta place j'userai d'une string (voir un record) et d'une TStringList et j'afficherai le contenu dans le panel et la listbox une fois le ping effectué.
0
MiniApp Messages postés 654 Date d'inscription lundi 21 juillet 2014 Statut Membre Dernière intervention 22 février 2019 5
Modifié par MiniApp le 30/07/2014 à 15:50
J'ai modifiez le code :
procedure TForm2.BitBtn1Click(Sender: TObject);
Var
Byte1:Byte;
Byte2:Byte;
Byte3:Byte;
Byte4:Byte;
TempList:TStrings;
TempIP:String;
begin
TempList := TStrings.Create;
ListBox1.Clear;
ListBox1.Items.Add('Chargement des IP en cours...');
ShowMessage('Le serveur à 25ms pour répondre');
TempList.Capacity := High(Integer);
For Byte1 := 0 To 255 do
Begin
For Byte2 := 0 To 255 do
Begin
For Byte3 := 0 To 255 do
Begin
For Byte4 := 0 To 255 do
Begin
TempIP := IntToStr(Byte1)+'.'+IntToStr(Byte2)+'.'+IntToStr(Byte3)+'.'+IntToStr(Byte4);
PanelIP.Caption := 'IP actuel ' + TempIP;
If Pos('[',Form1.RunDosInMemo('Ping -n 1 -w 25 -4 '+TempIP).Text) <> 0 Then
TempList.Add(TempIP);
end;
end;
end;
end;
ListBox1.Clear;
ListBox1.Items.Add('---Liste des IP positifs---');
ListBox1.Items.AddStrings(TempList);
end;

Mais cela n'a rien changez question vitesse. :'-(.
Les 4 For To Do me simplifie le code


Chercher et essayer : vous trouverez la solution!
Fouiner et regarder partout : vous trouverez la connaissance!!
0
Les For To Do te simplifient ton code mais ils le rendent plus lent aussi. Est ce que tu es conscient du nombre de cycles machine que cela implique ;) ?

J'ai lu tes interventions et tu penses que la taille d'un code et la vitesse d'execution sont liées : C'est faux. Un code de quelques lignes ne rime pas avec rapidité.

Ceci dit un simple calcul 256^4*25/1000=107374182,4 secondes ce qui fait quand même 1240 jours. De ce fait ce que tu cherches à faire est déjà à la base voué à l'échec. Ton problème est que tu conçoit surement mal à la base ce que tu veux faire. Est tu obligé de balayer toutes les IP ? Cela m'étonnerait fortement. Diminue le nombre d'ip que tu veux pinger, supprime ces for qui alourdissent ton code, supprime ton panel et cela ira beaucoup mieux :).
0
Francky23012301 Messages postés 400 Date d'inscription samedi 6 août 2005 Statut Membre Dernière intervention 11 février 2016 1
31 juil. 2014 à 03:47
Et je parle même pas de ton formatage IntToStr(Byte1)+'.'+IntToStr(Byte2)+'.'+IntToStr(Byte3)+'.'+IntToStr(Byte4); qui est horrible et surtout absolument pas optimisé
0
MiniApp Messages postés 654 Date d'inscription lundi 21 juillet 2014 Statut Membre Dernière intervention 22 février 2019 5
Modifié par MiniApp le 31/07/2014 à 11:11
J'ai réduit comme tu as dit les actions sur des composants visibles ensuite je ne fait plus qu'une seule fois
(Byte1)+'.'+IntToStr(Byte2)+'.'+IntToStr(Byte3)+'.'+IntToStr(Byte4);
.
Après j'ai alloué plus de mémoire dans le TempList via
TempList.Capacity := High(Integer);
ce qui évite à chaque ajout de devoir réalloué de la mémoire
Sinon je peux essayer de crée des threads mais je ne sais pas comment faire. Autrement je crée 255^4 processus que je gère.
0
MiniApp Messages postés 654 Date d'inscription lundi 21 juillet 2014 Statut Membre Dernière intervention 22 février 2019 5
Modifié par MiniApp le 1/08/2014 à 13:38
Que me propose tu (je cherche à connaître les IP où je peut me connecter) ?

Quel est le problème des For To Do imbriquer (j'ai demander à un amis qui me dit que tant que pour chaque For To Do j'utilise une variable différente il n'y a pas de problème) ?

Pour la solution des Threads je peut éventuellement gérez les thread et n'en crée que si la mémoire le peut.

Chercher et essayer : vous trouverez la solution!
Fouiner et regarder partout : vous trouverez la connaissance!!
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cptpingu Messages postés 3840 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 23 août 2024 126
Modifié par cptpingu le 1/08/2014 à 15:56
Bonjour.

Je viens parler de manière plus générale, n'ayant pas fait de Delphi depuis quelques années (néanmoins les bonnes pratiques sont les mêmes partout).

Je vais essayer de compléter la réponse de Francky, en détaillant ce qu'il essaie de te dire, et esquisser une réponse.

- Au sujet des boucles:
Connais-tu la complexité algorithmique ? La notation de Landau ? Pour faire simple, une boucle a "généralement" une complexité de 0(n), une double boucle a "généralement" une complexité de O(n^2), etc... des boucles imbriquées impliquent "généralement" un degré supplémentaire de complexité (et donc un exponentiel de lenteur supplémentaire).
Je prend un exemple:
Si tu as 10 éléments à faire tourner avec un algo en 0(n), 0(n^2) et O(n^3), il te faudra respectivement:
- 0(n) = O(10) => 10 étapes
- 0(n^2) = O(10^2) = O(100) => 100 étapes
- 0(n^3) = O(10^3) = O(1000) => 1000 étapes

Pour la même entrée, un algorithme en O(n) est exponentiellement plus rapide qu'un algorithme en O(n^2) ou 0(n^3).
Avec de l'expérience, on repère très vite ceci. Raison pour laquelle Francky a immédiatemment "tiqué".

Règle générale => J'ai plus de 2 niveaux d'imbrication de boucles == je me pose des questions.

- Au sujet des boucles que tu as choisis:
Pour le cas très particulier que tu as choisis, c'est-à-dire, générer une suite fini, avoir 4 boucles n'augmentent pas la complexité algorithmique.
Car 256 * 256 * 256 * 256 équivaut strictement à faire une boucle de 1 -> 4294967296.
À noter que c'est du au fait que ton nombre d'entrées n'est pas variable, la complexité devenant alors constante, et qu'il n'y a pas "d'overlapping" de tes passes (c-à-d tu ne repasses pas deux fois par la même "case"). C'est donc une combinatoire.

- Au sujet du: (Byte1)+'.'+IntToStr(Byte2)+'.'+IntToStr(Byte3)+'.'+IntToStr(Byte4);
Non, tu ne l'appelles pas 1 fois, mais 4294967296 fois ! Donc si cette instruction est mal optimisée, ça fait mal...
On ne concatène jamais des strings avec + (et c'est vrai dans beaucoup de langage). En effet, c'est viable pour 2 strings, mais pour n strings c'est une horreur. Tu vas générer autant de chaines temporaires qu'il y a d'éléments.
Je m'explique, avec un exemple:
Voici ce qui se passe avec "res = a + b + c + d":
1) a + b => a'
2) a' + c + d
3) a' + c => c'
4) c' + d
5) c' + d => d'
6) res = d' (avec la création "invisible" et temporaire de a', c' et d')

Voici ce qui se passe maintenant avec un StringBuffer/std::ostringstream (je ne connais pas le nom utilisé en Delphi):
buffer buf;
buf.append(a);
buf.append(c);
buf.append(d);
buf.append(e);
res = buf.toString();

1) append des addresses de a, b, c et d.
2) res = buf.toString() (réunion des addresses en une seule passe, nombre de variable temporaire: 1 pour "buf", au lieu de n).

Inutile de préciser qu'en terme de vitesse tant qu'en mémoire prise, c'est largement mieux !

- Au sujet de la manière dont tu génères tes ips:
Ne pas oublier qu'une ip, ce n'est qu'un nombre ! La représentation textuelle n'est pas obligatoire.
Exemple:
95.211.238.122 <=> 1607724666
78.456.45.45 <=>1338518829
0.0.0.0 <=> 0
255.255.255.255 <=> 4294967295

Au final, il suffit de faire une seule boucle allant de 0 à 4294967295 pour avoir toutes tes ips.

- Au niveau des threads:
Oui, en coupant tes requêtes en thread, tu iras plus vite. Ne pas oublier qu'un vrai thread n'est efficace que sur un coeur où il est seul. Ne pas confondre concurrence et parallélisme.
Si tu as un seul coeur, avoir 10 threads veut dire: Chacun leur tour, il vont petit à petit avoir du temps alloué. Donc 10 threads vont avoir 1/10 de "puissance". Puisqu'il travaille (pour vulgariser) un cycle d'horloge sur 10.
Si tu as deux coeurs, avoir 10 threads veut dire: 5 threads sur chaque coeurs, et donc 1/5 de puissance par thread.

Donc en multi-threading, on prend généralement la règle: nombre de thread == nombre de coeur.
Je parle évidemment pour un calcul intense qui bouffe le cpu à fond.

Pour le réseau, comme durant le temps de réponse qui peut durer 100 ms, ton cpu ne fait pas grand chose, tu peux largement augmenter le nombre de thread (pas 1000 non plus, hein ! :p). Maintenant, le temps de réponse n'étant pas connu à l'avance, il faut choisir le nombre de threads de manière empirique (commencer avec 50, puis ajuster au fur et à mesure en surveillant son cpu). Il y a surement des méthodes plus précises et recherchées que cela, mais ce n'est pas mon domaine de prédilection.

- Au sujet de l'utilité de ton programme.

Désolé, mais le périmetre de ton projet n'est pas bien ficelé ! Tu vas interroger la terre entière (quasi litérallement !), et voir qui te répond. Ce n'est pas extraordinaire, et l'utilité est franchement limitée. À la limite interroger une plage restreinte, confinée à une entreprise, pourquoi pas. Mais n'espère pas interroger 4.2 milliards d'adresses avec un temps convenable (même si tu mets plein de thread et pleins de machines...).
Pour t'en rendre compte, fait une boucle de 0 à 4.2 milliards, ne faisant rien d'autre que d'afficher "ligne + i", pour t'apercevoir de l'énormité de ce nombre.


Je n'ai pas de réponse en Delphi, mais j'espère t'avoir éclairé.

__________________________________________________________________________________________________
Améliorez votre expérience CodeS-SourceS avec ce plugin:
http://codes-sources.commentcamarche.net/forum/affich-10000111-plugin-better-cs-2#cptpingu-signature
0
Francky23012301 Messages postés 400 Date d'inscription samedi 6 août 2005 Statut Membre Dernière intervention 11 février 2016 1
Modifié par Francky23012301 le 1/08/2014 à 15:49
Merci Cptpingu pour cette réponse détaillée et argumentée (J'en avais malheureusement pas le courage méa culpa).

Pour les threads pour donner à mon tour une réponse plus détaillée : Effectivement tu peux utiliser les threads. Ceci dit il y a une limitation en nombre qui dépend de la capacité de ta "machine" : L'article cité en lien t'explique très bien la chose avec le calcul théorique correspondant. Créer une routine qui créer un nombre de threads en fonction de la capacité de la machine est possible : Mais c'est loin d'être simple du tout car il faut que tu ailles surveiller le CPU à chaque instant et que tu t'amuses à construire et libérer des threads en fonction du résultat obtenu,y compris des threads qui sont entrain de fonctionner et qui n'ont peut etre pas encore recu la réponse du ping lancé. C'est un travail très complexe qui t'attend et qui n'est pas à la portée de n'importe qui :).

Pour finir : même avec un pc récent et puissant tu ne pourras pas créer 4294967296 threads. Tu comprends bien que même si tu utilises 1000 threads ce que tu cherche à faire prendra au moins une journée.

Un thread utilise de la mémoire : Un nombre élevé de threads fait que la quantité de mémoire utilisée sera telle que ton système risque de devenir très lent voir même de freezer jusqu'à en arriver à se bloquer impliquant que tu auras gagné le contraire de ce que tu souhaites atteindre par l'utilisation de threads massifs. Donc là aussi faut faire attention car tu risques de bosser pour rien au final.

Pour le reste (la concaténation, les boucles ... ..) rien a ajouter : CptPingu t'a parfaitement expliquer les raisons de ma remarque sur ces deux sujets.

Pour finir : la politique des FAI, le tien FAI risque de ne pas du tout apprécier que tu utilises leur bande passante pour ce genre de chose. Pour du spam certains internautes se font fermer leur connection par leur FAI. Donc il faut faire attention à ce que l'on fait ;).

En lisant ton objectif il ressort que tu devrais avant toute chose te documenter car pinger toutes les IP comme tu le souhaites n'a concrètement aucun intérêt puisque certaines IP sont réservées et non accessibles. De plus se connecter à une IP ne veut rien dire ;). Donc concrètement tu veux faire quoi après avec ces pings valides ? Soit plus précis.
0
MiniApp Messages postés 654 Date d'inscription lundi 21 juillet 2014 Statut Membre Dernière intervention 22 février 2019 5
Modifié par MiniApp le 1/08/2014 à 16:07
Merci cptpingu pour tes explications. Pour l'IP sans "." je vais essayer. Et je pense que je vais plutôt interroger tout le sous réseau entier "192.168." (65536 IP voir 256 IP) (
(Byte1)+'.'+IntToStr(Byte2)+'.'+IntToStr(Byte3)+'.'+IntToStr(Byte4)
; je disait que je l'apelle 1 fois par boucle). J'avais essayer là tout de suite une technique beaucoup plus rapide qui consistait à crée des TClientSocket et de les détruire ce qui faisait littéralement une surcharge des resources (je m'en suis douter), et en plus ça ne marchait pas :-(. Merci à vous 2.

Chercher et essayer : vous trouverez la solution!
Fouiner et regarder partout : vous trouverez la connaissance!!
0
MiniApp Messages postés 654 Date d'inscription lundi 21 juillet 2014 Statut Membre Dernière intervention 22 février 2019 5
4 août 2014 à 14:57
Au final mon code renvoie toutes les IPs. C'est vraiment pas top.
:'-(
0
Rejoignez-nous