MiniApp
Messages postés654Date d'inscriptionlundi 21 juillet 2014StatutMembreDernière intervention22 février 2019
-
Modifié par MiniApp le 30/07/2014 à 13:51
MiniApp
Messages postés654Date d'inscriptionlundi 21 juillet 2014StatutMembreDernière intervention22 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;
{ 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!!
- 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.
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 ;)
Francky23012301
Messages postés400Date d'inscriptionsamedi 6 août 2005StatutMembreDernière intervention11 février 20161 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é.
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!!
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 :).
Francky23012301
Messages postés400Date d'inscriptionsamedi 6 août 2005StatutMembreDernière intervention11 février 20161 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é
.
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.
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!!
Vous n’avez pas trouvé la réponse que vous recherchez ?
cptpingu
Messages postés3840Date d'inscriptiondimanche 12 décembre 2004StatutModérateurDernière intervention23 août 2024126 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é.
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.
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) (
; 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!!