On Tue, 24 Mar 2026 05:41:27 GMT, David Holmes <[email protected]> wrote:

>> 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 |             —...
>
> src/java.base/share/classes/java/lang/Thread.java line 1793:
> 
>> 1791:      * @see        #getName
>> 1792:      */
>> 1793:     public final void setName(String name) {
> 
> You can't get rid of synchronization here as the method needs to be atomic 
> wrt. setting the Java level name and the native name.

Ok. Removing the synchronization actually doesn't do much, we are talking about 
~10ns improvement. And even that is likely to disappear when lock elision kicks 
in. The only scenario where this would make a difference is when the setName() 
calls were heavily contended (I am trying to check with the customer if that 
has been the case), but as you say, in this case we probably expect consistency 
and can't do much.

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

PR Review Comment: https://git.openjdk.org/jdk/pull/30374#discussion_r2980670309

Reply via email to