Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix performance of WinProcess.getEnvironmentVariables() #69

Merged
merged 7 commits into from
Sep 26, 2022

Conversation

jtnord
Copy link
Member

@jtnord jtnord commented Jan 31, 2022

fixes #68 / JENKINS-67681

The performance of the code was no longer efficient in a Java5 world where String.substring involves memory copying to prevent holding onto larger strings preventing GC.

Benchmark showed the following:

# Run complete. Total time: 00:05:01

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                          Mode  Cnt  Score   Error  Units
ProcessEnvironmentVariableBenchmark.testNativeRaw  avgt    5  0.122 ± 0.024  ms/op
ProcessEnvironmentVariableBenchmark.testNew        avgt    5  0.135 ± 0.021  ms/op
ProcessEnvironmentVariableBenchmark.testOriginal   avgt    5  1.069 ± 0.112  ms/op

the java code used to take approximately .9ms per call and now takes approx 0.01ms

test code was only checking that the returned environment variable map
ontained the "TEST" environment variable, however if the code returned
incorrectly then the test would still pass.

This adds a unit test to ensure future changes do not regress behaviour
@@ -159,36 +157,23 @@ private void parseCmdLineAndEnvVars() {
String s = Native.getCmdLineAndEnvVars(pid);
if(s==null)
throw new WinpException("Failed to obtain for PID="+pid);
int sep = s.indexOf('\0');
commandline = s.substring(0,sep);
envVars = new TreeMap<String,String>(CASE_INSENSITIVE_COMPARATOR);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the old CASE_INSENSITIVE_COMPARATOR was inefficient - running String.toUpperCase repeatedly on insertion.

Comment on lines -175 to -169
} else {
t = s.substring(0,sep);
s = s.substring(sep+1);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the other part of the inefficiency was just chopping the string down, and creating substrings from it. Far more efficient to scan the entire String once and chop it.

if (end != -1) {
s = s.substring(0, end);
}
StringTokenizer st = new StringTokenizer(s, "\0", false);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

despite the fact that javadoc points you to use String.split(string regexp) it is no where near as performant according to the benchmark.

}
StringTokenizer st = new StringTokenizer(s, "\0", false);
commandline = st.nextToken();
envVars = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using String.CASE_INSENSITIVE_ORDER which can shortcut the comparison as it uses String.compareIgnoreCase() which can in the optimim just look at the first character, so avoids an entire upper-casing of each string on comparison.

Comment on lines +57 to +63
static {
ARCHITECTURE_DEPENDANT_ENVIRONMENT_VARS = new HashSet<>();
ARCHITECTURE_DEPENDANT_ENVIRONMENT_VARS.add("PROCESSOR_ARCHITEW6432");
ARCHITECTURE_DEPENDANT_ENVIRONMENT_VARS.add("PROCESSOR_ARCHITECTURE");
ARCHITECTURE_DEPENDANT_ENVIRONMENT_VARS.add("PROGRAMFILES");
ARCHITECTURE_DEPENDANT_ENVIRONMENT_VARS.add("COMMONPROGRAMFILES");
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there may be more - these where what I observed on my windows 11 x64 environment.

Map<String, String> processEnv = wp.getEnvironmentVariables();

// environment variable that start with = is just some funky stuff!
// and there are some that start with "=" that won't show up in set e.g. =ExitCode =CLINK.SCRIPTS
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not find any documentation on this (starting with =, but they exist and are funky!
so just excluded them from the test

e.g.

set
echo %=ExitCode%

@oleg-nenashev oleg-nenashev self-requested a review January 31, 2022 15:13
Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, bumping to Java 1.8 is perfectly fine at this moment.
Note that I do not longer have release environment for Windows components and Maven Central. Please contact @batmat and @MarkEWaite to discuss who will take care of thios component, and I will help with getting permissions. Bonus points for a Jenkins devlist thread

src/main/java/org/jvnet/winp/WinProcess.java Outdated Show resolved Hide resolved
@jtnord
Copy link
Member Author

jtnord commented Jan 31, 2022

Note that I do not longer have release environment for Windows components and Maven Central.

Are you the only maintainer currently @oleg-nenashev? As the module appears to be used outside of Jenkins (still in my Idea distrubution) is the Jenkins dev-list the best place for this to happen, as the Jenkins needs may well be different from others.

@jtnord
Copy link
Member Author

jtnord commented Feb 11, 2022

Note that I do not longer have release environment for Windows components and Maven Central. Please contact @batmat and @MarkEWaite to discuss who will take care of thios component, and I will help with getting permissions. Bonus points for a Jenkins devlist thread

https://groups.google.com/g/jenkinsci-dev/c/JmdP7Z0_ppc

Also cc @segrey

Copy link
Member

@basil basil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

@basil basil merged commit 2c33aff into jenkinsci:master Sep 26, 2022
@jtnord
Copy link
Member Author

jtnord commented Sep 27, 2022

Thanks for merging Basil, I had forgotten about this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

obtaining the environmnet variables is inefficent
3 participants