diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e9771ef..df0331f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -44,6 +44,7 @@ steps: copy native-powershell\host.h .\pkg\powershell\ ; copy native-powershell\x64\Release\psh_host.dll . ; copy native-powershell\x64\Release\psh_host.dll .\pkg\powershell\ ; + copy native-powershell\x64\Release\psh_host.dll .\tests\benchmarks\ ; mkdir c:\go_tmp; $env:GOPATH="c:\go_tmp"; $env:GOBIN=$env:GOPATH + "\bin"; diff --git a/go.mod b/go.mod index 933847d..9724b0a 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module github.com/KnicKnic/go-powershell go 1.12 -require golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb +require ( + golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb + k8s.io/klog v1.0.0 +) diff --git a/go.sum b/go.sum index 7803b1a..083a288 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,5 @@ +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= diff --git a/tests/benchmarks/benchmark_test.go b/tests/benchmarks/benchmark_test.go new file mode 100644 index 0000000..a6d3f0f --- /dev/null +++ b/tests/benchmarks/benchmark_test.go @@ -0,0 +1,105 @@ +package benchmarks + +// this file is used to help benchmark the impact of recreating the runspace every time, or if we should cache them. +// go test . -count 1 -v -run .*Power.* + +import ( + "testing" + + "sync" + + "github.com/KnicKnic/go-powershell/pkg/powershell" +) + +func executePowershellFullCreate(script string, namedArgs map[string]interface{}) (obj []string, err error) { + + runspace := powershell.CreateRunspaceSimple() + defer runspace.Close() + return executePowershellCommon(runspace, script, namedArgs) +} + +func executePowershellBuffered(script string, namedArgs map[string]interface{}) (obj []string, err error) { + runspace := <-runspaceInstances + defer func() { runspaceInstances <- runspace }() + + return executePowershellCommon(runspace, script, namedArgs) +} + +func executePowershellCommon(runspace powershell.Runspace, script string, namedArgs map[string]interface{}) (obj []string, err error) { + + results := runspace.ExecScript(script, true, namedArgs) + defer results.Close() + + if !results.Success() { + exception := results.Exception.ToString() + panic(exception) + //return []string{}, errors.New(exception) + } + + strResults := make([]string, len(results.Objects), len(results.Objects)) + for i, result := range results.Objects { + strResults[i] = result.ToString() + } + + return strResults, nil +} + +// after running a few tests, I do not believe that buffering powershell sessions is worth the complexity of the code. +// with a 10 unit worker pool, running 100 powershell tests took .65 seconds +// sequentially running 100 tests and creating and destroying the runspace every time it took 3.26 seconds + +var runspaceInstances chan powershell.Runspace + +func init() { + runspaceInstanceCount := 10 + runspaceInstances = make(chan powershell.Runspace, runspaceInstanceCount) + for i := 0; i < runspaceInstanceCount; i++ { + runspaceInstances <- powershell.CreateRunspaceSimple() + } +} + +var powershellTestRunSize int = 100 + +var powershellCommand = "start-sleep -milliseconds 100" + +// var powershellCommand = "$a = 1+1" + +func Test_LotsPowershellParallelBuffered(t *testing.T) { + + var wg sync.WaitGroup + simplePowershellCommand := func(wg *sync.WaitGroup) { executePowershellBuffered(powershellCommand, nil); wg.Done() } + + for i := 0; i < powershellTestRunSize; i++ { + wg.Add(1) + go simplePowershellCommand(&wg) + } + wg.Wait() + +} +func Test_LotsPowershellParallel(t *testing.T) { + + var wg sync.WaitGroup + simplePowershellCommand := func(wg *sync.WaitGroup) { executePowershellFullCreate(powershellCommand, nil); wg.Done() } + + for i := 0; i < powershellTestRunSize; i++ { + wg.Add(1) + go simplePowershellCommand(&wg) + } + wg.Wait() + +} + +func Test_LotsPowershellSequentialBuffered(t *testing.T) { + + for i := 0; i < powershellTestRunSize; i++ { + executePowershellBuffered(powershellCommand, nil) + } + +} + +func Test_LotsPowershellSequential(t *testing.T) { + + for i := 0; i < powershellTestRunSize; i++ { + executePowershellFullCreate(powershellCommand, nil) + } +}