Skip to content

Instantly share code, notes, and snippets.

@egonelbre
Created November 14, 2016 12:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save egonelbre/d4c8bcc7e61b32fd42bedb2d6847b57a to your computer and use it in GitHub Desktop.
Save egonelbre/d4c8bcc7e61b32fd42bedb2d6847b57a to your computer and use it in GitHub Desktop.
shell32 load time measurement
rem don't use optimizations otherwise cl may remove Shell32 dependency
cl dynamic.cpp
cl static.cpp
cl none.cpp
del *.obj
rem compiled x64 https://dl.dropboxusercontent.com/u/4300994/Go/measure-shell32-loading.zip
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <Windows.h>
#include <shellapi.h>
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HINSTANCE proc = LoadLibrary("shell32.dll");
return 0;
}
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <Windows.h>
#include <shellapi.h>
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
return 0;
}
#pragma comment(lib, "Shell32")
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <Windows.h>
#include <shellapi.h>
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// to ensure that Shell32 gets actually linked
auto x = ShellExecute;
return 0;
}
package main
import (
"errors"
"flag"
"fmt"
"os"
"runtime"
"strings"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
var (
N = flag.Int("n", 1000, "number of measurements")
)
type Measurement struct {
N int
Elapsed time.Duration
Kernel time.Duration
User time.Duration
PageFault int
WorkingSet int
PagedPool int
NonPagedPool int
PageFileSize int
}
func (m *Measurement) Add(b *Measurement) {
m.N += b.N
m.Elapsed += b.Elapsed
m.Kernel += b.Kernel
m.User += b.User
m.PageFault += b.PageFault
m.WorkingSet += b.WorkingSet
m.PagedPool += b.PagedPool
m.NonPagedPool += b.NonPagedPool
m.PageFileSize += b.PageFileSize
}
func (m *Measurement) Print() {
fmt.Printf("Elapsed %3.6fms\n", (m.Elapsed/time.Duration(m.N)).Seconds()*1000)
fmt.Printf("Kernel %3.6fms\n", (m.Kernel/time.Duration(m.N)).Seconds()*1000)
fmt.Printf("User %3.6fms\n", (m.User/time.Duration(m.N)).Seconds()*1000)
fmt.Printf("PageFault %3v KB\n", m.PageFault/m.N)
fmt.Printf("WorkingSet %3v KB\n", m.WorkingSet/m.N)
fmt.Printf("PagedPool %3v KB\n", m.PagedPool/m.N)
fmt.Printf("NonPagedPool %3v KB\n", m.NonPagedPool/m.N)
fmt.Printf("PageFileSize %3v KB\n", m.PageFileSize/m.N)
}
func filetimeToDuration(ft windows.Filetime) time.Duration {
nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime)
return time.Duration(nsec*100) * time.Nanosecond
}
func Inspect(pid windows.Handle) (*Measurement, error) {
m := &Measurement{N: 1}
var creation, exit, kernel, user windows.Filetime
if err := windows.GetProcessTimes(pid, &creation, &exit, &kernel, &user); err != nil {
return nil, err
}
m.Elapsed = filetimeToDuration(exit) - filetimeToDuration(creation)
m.Kernel = filetimeToDuration(kernel)
m.User = filetimeToDuration(user)
var pmc ProcessMemoryCounters
if err := GetProcessMemoryInfo(pid, &pmc); err != nil {
return nil, err
}
m.PageFault = int(pmc.PageFaultCount)
m.WorkingSet = int(pmc.PeakWorkingSetSize)
m.PagedPool = int(pmc.QuotaPeakPagedPoolUsage)
m.NonPagedPool = int(pmc.QuotaPeakNonPagedPoolUsage)
m.PageFileSize = int(pmc.PeakPagefileUsage)
return m, nil
}
func MeasureProcess(args string) (*Measurement, error) {
si := &windows.StartupInfo{}
pi := &windows.ProcessInformation{}
argsp := windows.StringToUTF16Ptr(args)
defer runtime.KeepAlive(argsp)
err := windows.CreateProcess(nil, argsp, nil, nil, false, 0, nil, nil, si, pi)
if err != nil {
return &Measurement{}, err
}
defer windows.CloseHandle(pi.Thread)
defer windows.CloseHandle(pi.Process)
ev, err := windows.WaitForSingleObject(pi.Process, windows.INFINITE)
if err != nil {
return &Measurement{}, err
}
if ev != windows.WAIT_OBJECT_0 {
return &Measurement{}, errors.New("cannot wait for process")
}
return Inspect(pi.Process)
}
func main() {
flag.Parse()
args := strings.Join(flag.Args(), " ")
var total Measurement
for i := 0; i < *N; i++ {
m, err := MeasureProcess(args)
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
total.Add(m)
}
total.Print()
}
// psapi
var (
modpsapi = windows.NewLazySystemDLL("psapi.dll")
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
)
type ProcessMemoryCounters struct {
CB uint32
PageFaultCount uint32
PeakWorkingSetSize uintptr
WorkingSetSize uintptr
QuotaPeakPagedPoolUsage uintptr
QuotaPagedPoolUsage uintptr
QuotaPeakNonPagedPoolUsage uintptr
QuotaNonPagedPoolUsage uintptr
PagefileUsage uintptr
PeakPagefileUsage uintptr
}
func GetProcessMemoryInfo(h windows.Handle, pmc *ProcessMemoryCounters) (err error) {
r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(pmc)), uintptr(unsafe.Sizeof(*pmc)))
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment