From 472cf9fac6ced87b8254c1eaf2f8b967ccd7a1a0 Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Thu, 1 Sep 2022 14:06:22 -0500 Subject: [PATCH 01/10] Multithreading working - Made info_disk function activate in another thread in an attempt to decrease lag caused by it --- winfetch.ps1 | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/winfetch.ps1 b/winfetch.ps1 index 7a88581..0c27f63 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -738,7 +738,7 @@ function info_memory { # ===== DISK USAGE ===== -function info_disk { +$disk_script = { [System.Collections.ArrayList]$lines = @() Add-Type @' using System; @@ -798,7 +798,7 @@ namespace WinAPI } # Verification stage - $diskLetters = @() + $diskLetters = @("C:") foreach ($diskLetter in $allDiskLetters) { foreach ($showDiskLetter in $showDisks) { if ($diskLetter -eq $showDiskLetter -or $showDiskLetter -eq "*") { @@ -826,7 +826,7 @@ namespace WinAPI $usage = [math]::floor(($used / $total * 100)) [void]$lines.Add(@{ title = "Disk ($diskLetter)" - content = get_level_info "" $diskstyle $usage "$(to_units $used) / $(to_units $total)" + content = "$(to_units $used) / $(to_units $total) ($usage%)" }) } } @@ -834,6 +834,11 @@ namespace WinAPI return $lines } +function info_disk { + Wait-Job -Job $disk_job + return Receive-Job -Job $disk_job +} + # ===== POWERSHELL VERSION ===== function info_pwsh { @@ -1054,6 +1059,9 @@ if ($img -and -not $stripansi) { # write info +if ($config -like "disk"){ + $disk_job = Start-Job -Name info_disk -ScriptBlock $disk_script +} foreach ($item in $config) { if (Test-Path Function:"info_$item") { $info = & "info_$item" From 2983982754e2fdbc09fd49a5c516074a49a1464c Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Thu, 1 Sep 2022 15:18:10 -0500 Subject: [PATCH 02/10] Runspaces implemented - Should be "*easily*" expandable to include entire script for large performance gain --- winfetch.ps1 | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/winfetch.ps1 b/winfetch.ps1 index 0c27f63..ff381af 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -835,8 +835,8 @@ namespace WinAPI } function info_disk { - Wait-Job -Job $disk_job - return Receive-Job -Job $disk_job + while ($disk_job.IsCompleted -eq $false){} + return $PowerShell.EndInvoke($disk_job) } @@ -1058,10 +1058,18 @@ if ($img -and -not $stripansi) { } -# write info +# Create Runspaces if ($config -like "disk"){ - $disk_job = Start-Job -Name info_disk -ScriptBlock $disk_script + $Runspace = [runspacefactory]::CreateRunspace() + $PowerShell = [powershell]::Create() + $PowerShell.Runspace = $Runspace + $Runspace.Open() + $null = $PowerShell.AddScript($disk_script) + $disk_job = $PowerShell.BeginInvoke() } + + +# write info foreach ($item in $config) { if (Test-Path Function:"info_$item") { $info = & "info_$item" From b8fb45f8cdfe549030b6432b9085ab9b7469efd1 Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Sun, 4 Sep 2022 16:37:58 -0500 Subject: [PATCH 03/10] Implement Runspace Pools - Made the runspaces implementation expandable - Implemented runspace pools which speeds up multiple runspaces significantly - Single runspace slightly faster too - Added array in which a runspace is created for each entry that is enabled in the config - Allows for some entries which may be faster without runspaces to be executed on the main thread --- winfetch.ps1 | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/winfetch.ps1 b/winfetch.ps1 index ff381af..da3673b 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -738,7 +738,7 @@ function info_memory { # ===== DISK USAGE ===== -$disk_script = { +$Script_disk = { [System.Collections.ArrayList]$lines = @() Add-Type @' using System; @@ -835,8 +835,9 @@ namespace WinAPI } function info_disk { - while ($disk_job.IsCompleted -eq $false){} - return $PowerShell.EndInvoke($disk_job) + $Job = $Jobs | Where-Object { $_.name -eq "disk" } + while ($Job.IsCompleted -eq $false){} + return $Job.Runspace.EndInvoke($Job.Status) } @@ -1058,14 +1059,22 @@ if ($img -and -not $stripansi) { } -# Create Runspaces -if ($config -like "disk"){ - $Runspace = [runspacefactory]::CreateRunspace() - $PowerShell = [powershell]::Create() - $PowerShell.Runspace = $Runspace - $Runspace.Open() - $null = $PowerShell.AddScript($disk_script) - $disk_job = $PowerShell.BeginInvoke() +# Create Runspace Pool +# TODO: Add option in config to disable runspaces +$RunspacePool = [runspacefactory]::CreateRunspacePool(1, [int]$env:NUMBER_OF_PROCESSORS+1) +$RunspacePool.Open() +$Jobs = New-Object System.Collections.ArrayList +$RunspaceOps = @("disk") # Operations to execute on another thread + + +# Create and Execute Runspaces +foreach ($Op in $RunspaceOps){ + if($config -like $Op){ + $PowerShell = [powershell]::Create() + $PowerShell.RunspacePool = $RunspacePool + $null = $PowerShell.AddScript((Get-Variable Script_$Op).Value) + $Jobs.Add([PSCustomObject]@{Name = $Op; Runspace = $PowerShell; Status = $PowerShell.BeginInvoke()}) + } } From 048b427c7ac31669c61f474904da9dea4d9817b6 Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Mon, 5 Sep 2022 11:52:40 -0500 Subject: [PATCH 04/10] More work on expandability - Created $Vars and $Funcs - Contains all functions and variables the runspaces might need --- winfetch.ps1 | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/winfetch.ps1 b/winfetch.ps1 index da3673b..38afc99 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -1059,6 +1059,74 @@ if ($img -and -not $stripansi) { } +# Create Variable Array and Function Array to Pass into Runspaces +$Vars = @( + # Flags + @{ + Name = "stripansi" + Value = $stripansi + }, + @{ + Name = "cpustyle" + Value = $cpustyle + }, + @{ + Name = "memorystyle" + Value = $memorystyle + }, + @{ + Name = "diskstyle" + Value = $diskstyle + }, + @{ + Name = "batterystyle" + Value = $batterystyle + }, + @{ + Name = "showdisks" + Value = $showdisks + }, + @{ + Name = "showpkgs" + Value = $showpkgs + }, + # Variables + @{ + Name = "e" + Value = $e + }, + @{# truncate_line + Name = "ansiRegex" + Value = $ansiRegex + }, + @{# info_motherboard, info_computer, info_uptime, info_terminal, info_cpu, info_cpu_usage + Name = "cimSession" + Value = $cimSession + }, + @{# Remove if info_os is not converted + Name = "os" + Value = $os + }, + @{# Likely to be removed as seems to be unused + Name = "t" + Value = $t + } +) +$Funcs = @( + @{ + Name = "get_percent_bar" + Value = $Function:get_percent_bar + }, + @{ + Name = "get_level_info" + Value = $Function:get_level_info + }, + @{ + Name = "truncate_line" + Value = $Function:truncate_line + } +) + # Create Runspace Pool # TODO: Add option in config to disable runspaces $RunspacePool = [runspacefactory]::CreateRunspacePool(1, [int]$env:NUMBER_OF_PROCESSORS+1) From 24207082089f46157d0fdd1ddd1fc872eec165b7 Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Tue, 6 Sep 2022 09:35:46 -0500 Subject: [PATCH 05/10] Add config option/parameter - Instead of having a scriptblock and helper info function, the original info functions are converted to a scriptblock and the helper function was moved into the $info loop - Functionally the same while preserving the original functionality of the script - Added config option and parameter $runspaces where if enabled(default), runspaces are used. And if disabled, runspaces aren't used. - Disabling runspaces is very useful for debugging as step through in PowerShell ISE doesn't show operations within a runspace. --- winfetch.ps1 | 61 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/winfetch.ps1 b/winfetch.ps1 index 38afc99..1488f07 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -58,6 +58,8 @@ Configure which disks are shown, use '-showdisks *' to show all. .PARAMETER showpkgs Configure which package managers are shown, e.g. '-showpkgs winget,scoop,choco'. +.PARAMETER runspaces + Configure whether to run commands in multiple threads using Runspaces. .INPUTS System.String .OUTPUTS @@ -83,7 +85,8 @@ param( [ValidateSet("text", "bar", "textbar", "bartext")][string]$batterystyle = "text", [ValidateScript({$_ -gt 1 -and $_ -lt $Host.UI.RawUI.WindowSize.Width-1})][alias('w')][int]$imgwidth = 35, [array]$showdisks = @($env:SystemDrive), - [array]$showpkgs = @("scoop", "choco") + [array]$showpkgs = @("scoop", "choco"), + [bool]$runspaces = $true ) if (-not ($IsWindows -or $PSVersionTable.PSVersion.Major -eq 5)) { @@ -158,6 +161,10 @@ $defaultConfig = @' # disabling unused ones will improve speed # $ShowPkgs = @("winget", "scoop", "choco") +# Configure whether to run in multiple threads using runspaces +# Disabling will reduce speed, but may be useful for debugging +# $runspaces = $true + # Use the following option to specify custom package managers. # Create a function with that name as suffix, and which returns # the number of packages. Two examples are shown here: @@ -738,7 +745,7 @@ function info_memory { # ===== DISK USAGE ===== -$Script_disk = { +function info_disk { [System.Collections.ArrayList]$lines = @() Add-Type @' using System; @@ -834,12 +841,6 @@ namespace WinAPI return $lines } -function info_disk { - $Job = $Jobs | Where-Object { $_.name -eq "disk" } - while ($Job.IsCompleted -eq $false){} - return $Job.Runspace.EndInvoke($Job.Status) -} - # ===== POWERSHELL VERSION ===== function info_pwsh { @@ -1103,11 +1104,11 @@ $Vars = @( Name = "cimSession" Value = $cimSession }, - @{# Remove if info_os is not converted + @{# TODO: Remove if info_os is not converted Name = "os" Value = $os }, - @{# Likely to be removed as seems to be unused + @{# TODO: Likely to be removed as seems to be unused Name = "t" Value = $t } @@ -1127,21 +1128,23 @@ $Funcs = @( } ) -# Create Runspace Pool -# TODO: Add option in config to disable runspaces -$RunspacePool = [runspacefactory]::CreateRunspacePool(1, [int]$env:NUMBER_OF_PROCESSORS+1) -$RunspacePool.Open() -$Jobs = New-Object System.Collections.ArrayList -$RunspaceOps = @("disk") # Operations to execute on another thread - - -# Create and Execute Runspaces -foreach ($Op in $RunspaceOps){ - if($config -like $Op){ - $PowerShell = [powershell]::Create() - $PowerShell.RunspacePool = $RunspacePool - $null = $PowerShell.AddScript((Get-Variable Script_$Op).Value) - $Jobs.Add([PSCustomObject]@{Name = $Op; Runspace = $PowerShell; Status = $PowerShell.BeginInvoke()}) + +# Check if Runspace config option is enabled +if ($Runspaces){ + # Create Runspace Pool + $RunspacePool = [runspacefactory]::CreateRunspacePool(1, [int]$env:NUMBER_OF_PROCESSORS+1) + $RunspacePool.Open() + $Jobs = New-Object System.Collections.ArrayList + $RunspaceOps = @("disk") # Operations to execute on another thread + + # Create and Execute Runspaces + foreach ($Op in $RunspaceOps){ + if($config -like $Op){ + $PowerShell = [powershell]::Create() + $PowerShell.RunspacePool = $RunspacePool + $null = $PowerShell.AddScript((Get-ChildItem Function:"info_$Op").ScriptBlock) + $Jobs.Add([PSCustomObject]@{Name = $Op; Runspace = $PowerShell; Status = $PowerShell.BeginInvoke()}) + } } } @@ -1149,7 +1152,13 @@ foreach ($Op in $RunspaceOps){ # write info foreach ($item in $config) { if (Test-Path Function:"info_$item") { - $info = & "info_$item" + if ($Runspaces -and $item -in $RunspaceOps){ + $Job = $Jobs | Where-Object { $_.name -eq $item } + while ($Job.IsCompleted -eq $false){} + $info = $Job.Runspace.EndInvoke($Job.Status) + }else{ + $info = & "info_$item" + } } else { $info = @{ title = "$e[31mfunction 'info_$item' not found" } } From 2b2a87cf71e2ba6df5d9662fff58411690cd92c3 Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Tue, 6 Sep 2022 11:23:48 -0500 Subject: [PATCH 06/10] Add variables and functions to runspaces - Adds $Vars and $Funcs into the runspace - Adds helper script into the beginning of the runspaces to declare needed variables and functions - Currently declares all of the variables and functions for every runspace, which is inefficient. Could be solved with much increased complexity if performance issues arise from it. - Reverted changes to info_disk as the issues were solved by passing variables in --- winfetch.ps1 | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/winfetch.ps1 b/winfetch.ps1 index 1488f07..f7f0f0b 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -805,7 +805,7 @@ namespace WinAPI } # Verification stage - $diskLetters = @("C:") + $diskLetters = @() foreach ($diskLetter in $allDiskLetters) { foreach ($showDiskLetter in $showDisks) { if ($diskLetter -eq $showDiskLetter -or $showDiskLetter -eq "*") { @@ -833,7 +833,7 @@ namespace WinAPI $usage = [math]::floor(($used / $total * 100)) [void]$lines.Add(@{ title = "Disk ($diskLetter)" - content = "$(to_units $used) / $(to_units $total) ($usage%)" + content = get_level_info "" $diskstyle $usage "$(to_units $used) / $(to_units $total)" }) } } @@ -1142,6 +1142,18 @@ if ($Runspaces){ if($config -like $Op){ $PowerShell = [powershell]::Create() $PowerShell.RunspacePool = $RunspacePool + $null = $PowerShell.AddScript({ + param($Vars, $Funcs) + # Import Variables + foreach ($Var in $Vars){ + Set-Variable -Name $Var.Name -Value $Var.Value + } + + # Import Functions + foreach ($Func in $Funcs){ + Set-Item -Path "Function:$($Func.Name)" -Value $Func.Value + } + }).AddArgument($Vars).AddArgument($Funcs) $null = $PowerShell.AddScript((Get-ChildItem Function:"info_$Op").ScriptBlock) $Jobs.Add([PSCustomObject]@{Name = $Op; Runspace = $PowerShell; Status = $PowerShell.BeginInvoke()}) } From ff180afc1eaf4b5d509cb9b5b7ca802857801e3d Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Tue, 6 Sep 2022 11:59:27 -0500 Subject: [PATCH 07/10] Fix bug and close runspaces - Fixed bug on line 1160 - Closes and disposes of runspaces and the pool as it is good practice to prevent issues and conflicts --- winfetch.ps1 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/winfetch.ps1 b/winfetch.ps1 index f7f0f0b..7f2f8d0 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -1135,7 +1135,9 @@ if ($Runspaces){ $RunspacePool = [runspacefactory]::CreateRunspacePool(1, [int]$env:NUMBER_OF_PROCESSORS+1) $RunspacePool.Open() $Jobs = New-Object System.Collections.ArrayList - $RunspaceOps = @("disk") # Operations to execute on another thread + + # Operations to execute on another thread + $RunspaceOps = @("disk") # Create and Execute Runspaces foreach ($Op in $RunspaceOps){ @@ -1155,7 +1157,7 @@ if ($Runspaces){ } }).AddArgument($Vars).AddArgument($Funcs) $null = $PowerShell.AddScript((Get-ChildItem Function:"info_$Op").ScriptBlock) - $Jobs.Add([PSCustomObject]@{Name = $Op; Runspace = $PowerShell; Status = $PowerShell.BeginInvoke()}) + $null = $Jobs.Add([PSCustomObject]@{Name = $Op; Runspace = $PowerShell; Status = $PowerShell.BeginInvoke()}) } } } @@ -1168,6 +1170,7 @@ foreach ($item in $config) { $Job = $Jobs | Where-Object { $_.name -eq $item } while ($Job.IsCompleted -eq $false){} $info = $Job.Runspace.EndInvoke($Job.Status) + $Job.Runspace.Dispose() }else{ $info = & "info_$item" } @@ -1238,6 +1241,10 @@ if (-not $stripansi) { } else { Write-Output "`n" } +if($runspaces){ + $RunspacePool.Close() + $RunspacePool.Dispose() +} # ___ ___ ___ # | __/ _ \| __| From db797a379902a75027977a0631664d14edefd1df Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Thu, 8 Sep 2022 11:04:17 -0500 Subject: [PATCH 08/10] Set $RunspaceOps - Set $RunspaceOps to every operation that doesn't error - Having memory and battery enabled cause really weird issues sometimes --- winfetch.ps1 | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/winfetch.ps1 b/winfetch.ps1 index 7f2f8d0..1aee5c5 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -1137,7 +1137,34 @@ if ($Runspaces){ $Jobs = New-Object System.Collections.ArrayList # Operations to execute on another thread - $RunspaceOps = @("disk") + $RunspaceOps = @( + "title" + "dashes" + "os" + "computer" + "kernel" + "motherboard" + "custom_time" + "uptime" + "ps_pkgs" + "pkgs" + "pwsh" + "resolution" + "terminal" + "theme" + "cpu" + "gpu" + "cpu_usage" + #"memory" # Causes Errors + "disk" + #"battery" # Causes Errors + "locale" + "weather" + "local_ip" + "public_ip" + "blank" + "colorbar" + ) # Create and Execute Runspaces foreach ($Op in $RunspaceOps){ From 2e43db131ddda35ea6ebdaf069af32749ca6748e Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Thu, 8 Sep 2022 11:23:36 -0500 Subject: [PATCH 09/10] Move $RunspaceOps into config file --- winfetch.ps1 | 62 ++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/winfetch.ps1 b/winfetch.ps1 index 1aee5c5..b7f7426 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -163,7 +163,37 @@ $defaultConfig = @' # Configure whether to run in multiple threads using runspaces # Disabling will reduce speed, but may be useful for debugging -# $runspaces = $true +$runspaces = $true + +# Operations to execute in runspaces +$RunspaceOps = @( + "title" + "dashes" + "os" + "computer" + "kernel" + "motherboard" + "custom_time" + "uptime" + "ps_pkgs" + "pkgs" + "pwsh" + "resolution" + "terminal" + "theme" + "cpu" + "gpu" + "cpu_usage" + #"memory" # Causes Errors + "disk" + #"battery" # Causes Errors + "locale" + "weather" + "local_ip" + "public_ip" + "blank" + "colorbar" +) # Use the following option to specify custom package managers. # Create a function with that name as suffix, and which returns @@ -1136,36 +1166,6 @@ if ($Runspaces){ $RunspacePool.Open() $Jobs = New-Object System.Collections.ArrayList - # Operations to execute on another thread - $RunspaceOps = @( - "title" - "dashes" - "os" - "computer" - "kernel" - "motherboard" - "custom_time" - "uptime" - "ps_pkgs" - "pkgs" - "pwsh" - "resolution" - "terminal" - "theme" - "cpu" - "gpu" - "cpu_usage" - #"memory" # Causes Errors - "disk" - #"battery" # Causes Errors - "locale" - "weather" - "local_ip" - "public_ip" - "blank" - "colorbar" - ) - # Create and Execute Runspaces foreach ($Op in $RunspaceOps){ if($config -like $Op){ From 60f9108e66c4508abe9376698e393efff7092cc2 Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Thu, 8 Sep 2022 14:29:00 -0500 Subject: [PATCH 10/10] Some Final Tweaks - Added some comments and tweaked some existing ones - Removed $t from $Vars as it was unused --- winfetch.ps1 | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/winfetch.ps1 b/winfetch.ps1 index b7f7426..6c709e6 100644 --- a/winfetch.ps1 +++ b/winfetch.ps1 @@ -162,7 +162,8 @@ $defaultConfig = @' # $ShowPkgs = @("winget", "scoop", "choco") # Configure whether to run in multiple threads using runspaces -# Disabling will reduce speed, but may be useful for debugging +# Disabling might cause a speed decrease or increase, depending on your system and configuration +# Disabling will make debugging easier $runspaces = $true # Operations to execute in runspaces @@ -1134,13 +1135,9 @@ $Vars = @( Name = "cimSession" Value = $cimSession }, - @{# TODO: Remove if info_os is not converted + @{ Name = "os" Value = $os - }, - @{# TODO: Likely to be removed as seems to be unused - Name = "t" - Value = $t } ) $Funcs = @( @@ -1171,6 +1168,7 @@ if ($Runspaces){ if($config -like $Op){ $PowerShell = [powershell]::Create() $PowerShell.RunspacePool = $RunspacePool + # Add Helper Script to Runspace $null = $PowerShell.AddScript({ param($Vars, $Funcs) # Import Variables @@ -1194,9 +1192,13 @@ if ($Runspaces){ foreach ($item in $config) { if (Test-Path Function:"info_$item") { if ($Runspaces -and $item -in $RunspaceOps){ + # Get Runspace from $item $Job = $Jobs | Where-Object { $_.name -eq $item } + # Wait for Runspace to finish while ($Job.IsCompleted -eq $false){} + # Get Runspace Output $info = $Job.Runspace.EndInvoke($Job.Status) + # Close Runspace $Job.Runspace.Dispose() }else{ $info = & "info_$item" @@ -1268,7 +1270,9 @@ if (-not $stripansi) { } else { Write-Output "`n" } + if($runspaces){ + # Close and Dispose Runspace Pool $RunspacePool.Close() $RunspacePool.Dispose() }