I analyzed the performance of Thread.setName() in response to a customer 
workload running Cassandra, where Thread.setName() showed up (mostly because of 
rather pathetic use of setName() from Cassanda *sigh*).

Profiling showed that most time (around 75%) is spent in the actual syscall, so 
there are limits on what we can do. There is some fixed overheads like the cost 
for synchronized, and also some costs that scale with the length of the name, 
most importantly the UTF8 conversion.

I implemented the following improvements:
 - Removed synchronized from setName(), as suggested by some folks in the JBS 
issue. This saves ~15 nanoseconds. Not sure if the method could be called 
contended by Cassandra, if so, the savings might be much larger.
 - Almost all thread names are Latin1/ASCII, and there is no need to convert to 
UTF8 in that case. Also, the various OS APIs to set the thread name don't even 
seem to specify the character encoding. Avoiding the UTF8 conversion brings 
down the length-dependent costs. In many cases we can also pass down the 
backing array of the string and avoid copying.
 - When the name doesn't change, we can skip updating the native name, which 
makes setName() almost a no-op.
 - For truncating the name on Linux to 16 chars, instead of using snprintf with 
a pattern, we can simply stitch together the name directly (first 7 chars, last 
6 chars, 2 dots in between), this saves ~100ns.

In the end, we bring down performance for the small cases by ~7%, longer names 
by ~20% and completely removed the conversion overhead that primarily affected 
longer names.

  | Benchmark     | (length) | Baseline (ns/op) | Optimized (ns/op) | Change  |
  
|---------------|----------|------------------:|-------------------:|--------:|
  | setName       |        1 |    602.3 ±  2.0   |     561.9 ±  1.5   |  -6.7%  
|
  | setName       |        4 |    605.9 ±  2.1   |     570.2 ±  1.2   |  -5.9%  
|
  | setName       |       15 |    617.1 ±  2.7   |     570.4 ±  2.8   |  -7.6%  
|
  | setName       |       16 |    712.1 ±  6.0   |     569.4 ±  2.7   | -20.0%  
|
  | setName       |       50 |    757.9 ±  5.2   |     566.3 ±  4.6   | -25.3%  
|
  | setName       |      200 |    986.2 ±  2.7   |     569.9 ±  4.9   | -42.2%  
|
  | setNameSame   |        1 |             —     |       7.4 ±  0.0   |    —    
|
  | setNameSame   |        4 |             —     |       7.4 ±  0.0   |    —    
|
  | setNameSame   |       15 |             —     |       7.4 ±  0.0   |    —    
|
  | setNameSame   |       16 |             —     |       7.4 ±  0.0   |    —    
|
  | setNameSame   |       50 |             —     |       7.4 ±  0.0   |    —    
|
  | setNameSame   |      200 |             —     |       7.4 ±  0.0   |    —    
|


Testing:
 - [x] tier1
 - [ ] tier2

The failing GHA test in langtools seems unrelated.

-------------

Commit messages:
 - 8373554: Improve performance of Thread.setName()

Changes: https://git.openjdk.org/jdk/pull/30374/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=30374&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8373554
  Stats: 124 lines in 8 files changed: 106 ins; 0 del; 18 mod
  Patch: https://git.openjdk.org/jdk/pull/30374.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/30374/head:pull/30374

PR: https://git.openjdk.org/jdk/pull/30374

Reply via email to