调用自定义功能非常慢

时间:2018-03-09 17:39:46

标签: powershell

我在powershell(v4)上有一个新手问题。

鉴于以下代码,

  • 当我在循环中执行$ t = $ i时需要0.5s
  • 当我在循环中执行$ t = DoNothing($ i)需要11.5秒
  • 当我在循环中执行$ t = DoNothing $ i时需要11.5秒

如果我改变功能DoNothing的内容来做某事,那么时间不会延长。

显然我在某个地方犯了一个大错,但我无法看到它的位置。非常感谢帮助我理解我的错误。

    function DoNothing($val) 
{
    return $val
}

Write-Host "Script started..."
$elapsed = [System.Diagnostics.Stopwatch]::StartNew() 


for($i=1
     $i -le 100000
     $i++){
     #$t = $i
     #$t = DoNothing($i)
     $t = DoNothing $i
     }

Write-Host "Script complete."
Write-Host "Total Elapsed Time: $($elapsed.Elapsed.ToString())"

2 个答案:

答案 0 :(得分:2)

Good question. I believe this is just the performance overhead of making function calls in PowerShell, but I did some experimentation to check.

A few things I noticed:

1) Saving the file increased performance around 75% compared to just working out of the PowerShell ISE without saving the file (From 7.3 seconds to 4.3 seconds).

2) Turning DoNothing into a parameterless function improved the execution time by an additional ~25% (From 4.3 seconds to 3.3 seconds). This would be useful if creating $i as a global variable would save time, but I tested that as well and, sadly, it increased execution time to 4.7 seconds.

3) I thought maybe explicitly requesting $val to be passed in as an int would decrease execution time, but it didn't. Time increased by about 0.2 seconds.

4) Naming the -val parameter when calling DoNothing ($t = DoNothing -val $i) did not improve performance.

5) Using $val instead of return $val did not improve performance.

6) Using -lt instead of -le did not improve performance.

7) Adding DoNothing to a PS module with only the DoNothing function in it severely decreased performance (from 4.3 seconds to 15 seconds).

So, I think this is all due to function overhead. I don't see anything 'wrong' in your code.

This has been an interesting experiment and might change when I choose to use PowerShell functions in the future. I wonder how this compares to other scripting languages.

Out of curiosity, I ran these same operations using C# and the whole thing completed in one one-thousandth of a second. Code is below.

class Program
{
    private static int DoNothing(int val) {
        return val;
    }
    static void Main(string[] args)
    {
        System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
        for (int i = 1; i <= 100000; i++)
        {
            int t = DoNothing(i);
        }
        Console.WriteLine(watch.Elapsed.ToString());
        Console.ReadKey();
    }
}

Now I've gone a bit further than this. I figure if you really needed this to be faster, maybe you could throw the workload out to C# from PowerShell. This ended up being MUCH MUCH FASTER than the initial implementation in PowerShell, but slower than the C# only option. This code runs in ~ 0.03 seconds. See the code below.

PowerShell (calling the C#)

$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
Write-Host "Script started..."
$lib = [Reflection.Assembly]::LoadFile("C:\Users\you\source\ClassLibrary1\bin\Debug\ClassLibrary1.dll")
$type = $lib.GetType("ClassLibrary1.Class1")
$method = $type.GetMethod("MyFunction")
$o = [Activator]::CreateInstance($type)
$method.Invoke($o, $null)
Write-Host "Script complete."
Write-Host "Total Elapsed Time: $($elapsed.Elapsed.ToString())"

C# (doing the work)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary1
{
    public class Class1
    {
        public static int DoNothing(int val)
        {
            return val;
        }

        public static void MyFunction()
        {
            for (int i = 1; i <= 100000; i++)
            {
                int t = DoNothing(i);
            }
        }
    }
}

答案 1 :(得分:0)

有关信息,在Python中,同样的事情(使用函数DoNothing)需要0.01s。我不知道Python和Powershell函数处理之间的主要区别,但0.01s和11.5s的速度要快1k倍......

import datetime

def DoNothing(val):
    return val

print("Script started...")
elapsed = datetime.datetime.now()


for i in range (1,100000):
    #t = i
    t = DoNothing(i)

print("Script complete.")
print('Total Elapsed Time: '+str(datetime.datetime.now() - elapsed))