Question Pourquoi Powershell est si lent?


J'ai essayé de faire une chose simple avec PowerShell, trouver des fichiers qui prennent le plus de place sur le lecteur. j'ai utilisé ls + sort et ... ça m'a pris une éternité.

Parfois, j'utilise far manager et par rapport à PowerShell, il semble beaucoup plus rapide et stable.

Ok, il est basé sur .NET, mais .NET n'est pas si lent. Je m'attends à voir quelque chose de léger et de rapide! C'est console!

Une autre chose, j'aimerais avoir quelque chose comme IEnumerable dans PowerShell pour voir les résultats immédiatement. Est-il possible de réaliser? Cela peut aider un peu en attendant des résultats car parfois je pense que ça traîne.

MODIFIER

Je fais quelque chose comme ça

ls -Recurse -ErrorAction SilentlyContinue | sort -Property Size | select -First 10

Et je suppose que cela pourrait prendre des jours.

MODIFIER

Juste pour comparer.

Le code C # m'a pris environ 2 min. Pour sûr, ce n'est pas idéal et n'a pas traité tous les fichiers, mais il a traité au moins> 95%.

void Main()
{
    GetFilesSize(@"C:\").OrderByDescending(x => x).Take(10).ToList();
}

public IEnumerable<long> GetFilesSize(string directory)
{
    var accessDenied = false;
    var dirList = new string[0]; 
    try
    {
        dirList = Directory.GetDirectories(directory);
    }
    catch{
        accessDenied = true;
    }

    if(accessDenied) yield break;

    foreach (var dir in dirList)
    {
        foreach (var size in GetFilesSize(dir))
        {
            yield return size;
        }
    }

    foreach (var fileName in Directory.GetFiles(directory))
    {
        if(fileName.Length>=260) continue;
        yield return new FileInfo(fileName).Length;
    }
}

4
2018-05-13 01:47


origine


"Une autre chose ..." Non, s'il vous plaît, non! Ne posez pas une deuxième question, qui générera des réponses supplémentaires / différentes / sans rapport, dans la même question. Créez simplement une nouvelle question SuperUser. - TOOGAM
serait très utile de voir votre code qui est "si lent" parce que peut-être ce n'est pas PowerShell qui est lent, plutôt votre code! - SimonS
Bienvenue à Super utilisateur! S'il vous plaît essayer de poser une question à la fois (sinon votre question sera fermée comme trop large). - DavidPostill♦
@Ramhound Si vous lisez attentivement mes commentaires, vous pouvez voir que je me plains des performances de PowerShell, pas de .net. .NET est mentionné comme base de powershell qui fonctionne plus rapidement. Je ne suis pas sûr de la quantité totale de fichiers, j'essaie d'analyser le lecteur entier. Donc je suppose qu'il y a des milliers de fichiers. - Neir0
@Ramhound j'ai calculé: 556458 - Neir0


Réponses:


PowerShell est un programme écrit en .Net, mais il exploite les interfaces vers de nombreux interpréteurs et exécutants différents lorsqu'il est en cours d'exécution. C'est un shell, tout comme BASH, même s'il est écrit en C, cela ne dit rien des binaires et des scripts exécutés. Les exécutables peuvent être du code .Net, des commandes VDM / CMD, des commandes shell * nix, des invocations WMI, des interfaces API non gérées, des fichiers JAR ou toute autre chose. Ces choix affectent les performances du code exécuté dans le shell, et non le langage dans lequel le shell est écrit.

Maintenant, il semble que vous rencontrez des difficultés avec l'implémentation d'une commande spécifique. Donc, la meilleure question est de savoir pourquoi ls lent à trier lorsqu'il est appelé depuis PowerShell. Lorsque nous creusons plus profondément, nous trouvons que ls est un alias pour 'Get-ChildItem' qui renvoie un tableau d'objets contenant des objets System.IO.DirectoryInfo.

PS C:\Windows\system32> $x=Get-ChildItem ./
PS C:\Windows\system32> $x.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array 

PS C:\Windows\system32> $x[1].GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     DirectoryInfo                            System.IO.FileSystemInfo   

PS C:\Windows\system32>

Vous pouvez récupérer le ls résultat, puis canaliser cela dans un Sort-Object  appel et il se comportera largement comme le fait un IEnumerable.

Notez que IEnumerable ne fait rien pour les performances. Vous pouvez le confondre avec IQueryable, qui définit mais n'effectue pas de requête jusqu'à la toute dernière seconde, probablement après avoir été décoré avec des opérations de filtrage et de tri, comme le fait .Net via LinQ to Objects. Dans ce cas, Get-ChildItem ne proposant pas de moteur de requête optimisé ni de source de données indexée, vous ne pouvez pas vraiment comparer les opérations de base de données modernes avec les listes de répertoires.

Donc, finalement, essayez quelque chose comme: ls ./ -recurse | Sort-Object Name -descending Pour moi, en ciblant System32, cela prend environ 20 secondes pour traiter et trier 54430 fichiers.

Enfin, notez que vous prenez un gros coup de performance lorsque vous essayez d'énumérer un répertoire auquel vous n'avez pas personnellement accès, alors assurez-vous de ne pas récurer dans des endroits où vous n'êtes pas autorisé à aller, ou vous allez souffrir d'un 2 + seconde attente pour chacun.

J'espère que cela pourra aider.


10
2018-05-13 22:26



IEnumerable n'apporte pas de gain de performance mais permet de voir les résultats immédiatement. En outre, dans le cas de PowerShell, cela pourrait améliorer les performances car, par exemple, si "ls" renvoie IEnumerable, il ne charge pas tous les fichiers dans la mémoire, ce qui peut représenter une surcharge importante. - Neir0
J'ai essayé ls + sort comme vous l'avez mentionné dans votre article, je veux vérifier tous les fichiers sur le disque mais c'est sûr que c'est impossible ou je suppose que cela a pris quelques jours (vraiment, parce que je l'ai laissé le soir et à le matin il n'y a pas de résultats). Mais par exemple, si je vais utiliser des outils spéciaux pour vérifier l'espace de conduite, ils fonctionnent très vite. C'est mon point. Tout fonctionne, déplace, copie, recherche lentement dans powershell et c'est agaçant. - Neir0
essayez de rediriger la sortie vers un fichier. un disque de volume standard ne devrait pas prendre toute la nuit à énumérer. êtes-vous déjà à la limite de la RAM ou le disque est-il malsain? - Frank Thomas
Je ne suis pas sûr que quelque chose ne va pas avec le lecteur, je dois faire des tests supplémentaires. J'ai un autre outil (TreeSize) qui fonctionne assez vite, donc ce n'est pas si évident. - Neir0


PowerShell est conçu pour être pratique plutôt que rapide. C'est un compromis - il fonctionne dans les coulisses, donc l'utilisateur doit faire moins. Faire plus de travail le rend plus lent.

Vérifiez que votre code PowerShell est une ligne, pour faire plus que votre code C # dans 15 lignes.

Cela fait plus - même si vous ne l'utilisez pas.

ls sur Linux renvoie des chaînes, les chaînes sont simples et rapides. Votre code .Net ne conserve même pas le nom du fichier, il en conserve simplement la taille, et les nombres sont encore plus petits pour que même plus vite.

ls dans PowerShell, retourne les objets [FileInfo] et [DirectoryInfo] - chacun doit être créé, et chacun doit interroger le fichier pour remplir les autres champs tels que CreationTime et LastWriteTime et Extension and Length, et que les champs de temps doivent créer [DateTime] objets.

C'est beaucoup plus lent pour chaque fichier. Cela coûte d’activer d’autres options, même si vous ne les utilisez pas - votre code PowerShell peut changer pour prendre la taille des 10 premiers fichiers créés en janvier avec une simple modification, sans autre applet de commande ou outil, et toujours avec une ligne, le code C # devrait être réécrit en profondeur, interroger l'heure de création, porter à la fois l'heure de la création et la taille au tri, etc.

La raison pour laquelle vous ne voyez pas les résultats immédiatement est que vous | sort. Cela rend cela impossible. Que faire si vous avez commencé à produire des résultats immédiatement, mais que le dernier fichier trouvé doit être trié au premier plan? Alors la sortie serait fausse - IEnumerable ne peut rien faire à ce sujet, | sort doit rassembler chaque entrée avant de pouvoir produire quelque chose. Votre tri est plus rapide car il trie les petites choses

Votre code .Net peut effectuer le tri lui-même plus rapidement car il trie un énumérable de [long], il n'a pas à faire de recherche de propriétés.

Dans l'ensemble, votre code fait beaucoup moins, et faire moins prend moins de temps. Mais cela vous a pris plus de temps pour écrire et est moins flexible et plus ciblé. Un compromis.


2
2017-09-23 21:40