Hi David and Robbin, Thank you for reviewing this fix!
Best regards, Daniil On 10/8/19, 1:49 AM, "Robbin Ehn" <robbin....@oracle.com> wrote: Great, thanks! /Robbin On 2019-10-07 18:41, Daniil Titov wrote: > Hi Robbin, > > Yes, I ran my benchmark [1]. Please see below the output showing that the table was grown. > > ../jdk/build/linux-x64-release/images/jdk/bin/java -cp . -Xlog:thread+table=info ThreadStartupTest > Starting the test > [0.185s][info][thread,table] Grown to size:512 > The test finished. > Execution time:15673 ms > > > [1] https://cr.openjdk.java.net/~dtitov/tests/ThreadStartupTest.java > > Thanks! > Daniil > > > On 10/7/19, 12:34 AM, "Robbin Ehn" <robbin....@oracle.com> wrote: > > Hi Daniil, > > Yes, good, but: > > >> >> Testing: Mach5 tier1, tier2, and tier3 tests successfully passed. > >> And if you have not done so, you should test this with the benchmark you > >> have as > >> a stress test and see that this does what we think. > > Can you please test it with your benchmark, if you have not done so? > > /Robbin > > >> Thanks, Robbin > >> >> > >> >> Thank you! > >> >> > >> >> Best regards, > >> >> Daniil > >> >> > >> >> On 10/2/19, 3:26 PM, "David Holmes" <david.hol...@oracle.com> wrote: > >> >> > >> >> Hi Daniil, > >> >> On 3/10/2019 2:21 am, Daniil Titov wrote: > >> >> > Hi David and Robbin, > >> >> > > >> >> > Could we consider making the ServiceThread responsible for the > >> >> ThreadIdTable resizing in the similar way how > >> >> > it works for StringTable and ResolvedMethodTable, rather than > >> having > >> >> ThreadIdTable::add() method calling ThreadIdTable::grow()? > >> >> > As I understand It should solve the current issue and > >> address the > >> >> concern that the doing the resizing could be a relatively long and > >> >> > doing it without polling for safepoints or while the holding > >> >> Threads_lock is not desirable. > >> >> I originally rejected copying that part of the code from the other > >> >> tables as it seems to introduce unnecessary complexity. Having a > >> >> separate thread trying to grow the table when it could be > >> concurrently > >> >> having threads added and removed seems like it could introduce > >> hard to > >> >> diagnose performance pathologies. It also adds what we know to be a > >> >> potentially long running action to the workload of the service > >> thread, > >> >> which means it may also impact the other tasks the service thread is > >> >> doing, thus potentially introducing even more hard to diagnose > >> >> performance pathologies. > >> >> So this change does concern me. But go ahead and trial it. > >> >> Thanks, > >> >> David > >> >> > Thank you, > >> >> > Daniil > >> >> > > >> >> > > >> >> > On 10/2/19, 6:25 AM, "David Holmes" <david.hol...@oracle.com> > >> wrote: > >> >> > > >> >> > Hi Robbin, > >> >> > > >> >> > On 2/10/2019 7:58 pm, Robbin Ehn wrote: > >> >> > > Hi David, > >> >> > > > >> >> > >> What if the table is full and must be grown? > >> >> > > > >> >> > > The table uses chaining, it just means load factor tip > >> over what is > >> >> > > considered a good backing array size. > >> >> > > >> >> > Coleen raised a good question in a separate discussion, > >> which made me > >> >> > realize that once the table has been initially populated all > >> >> subsequent > >> >> > additions, and hence all subsequent calls to grow() always > >> happen > >> >> with > >> >> > the Threads_lock held. So we can't just defer the grow(). > >> >> > > >> >> > >> That aside, I'd like to know how expensive it is to > >> grow this > >> >> table. > >> >> > >> What are we talking about here? > >> >> > > > >> >> > > We use global counter which on write_synchronize must > >> scan all > >> >> > > threads to make sure they have seen the update (there some > >> >> > > optimization to avoid it if there is no readers at all). > >> Since this > >> >> > > table contains the threads, we get double penalized, for > >> each new > >> >> > > thread the synchronization cost increase AND the number > >> of items. > >> >> > > > >> >> > > With concurrent reads you still need many thousands of > >> threads, but > >> >> > > I think I saw someone mentioning 100k threads, assuming > >> concurrent > >> >> > > queries the resize can take hundreds of ms to finish. > >> Note that > >> >> reads > >> >> > > and inserts still in operate roughly at the same speed > >> while > >> >> > > resizing. So a longer resize is only problematic if we > >> do not > >> >> > > respect safepoints. > >> >> > I think if anything were capable of running 100K threads > >> we would be > >> >> > hitting far worse scalability bottlenecks than this. But > >> this does > >> >> seem > >> >> > problematic. > >> >> > > >> >> > Thanks, > >> >> > David > >> >> > ----- > >> >> > > >> >> > > Thanks, Robbin > >> >> > > > >> >> > >> > >> >> > >> David > >> >> > >> > >> >> > >>> /Robbin > >> >> > >>> > >> >> > >>> On 2019-10-02 08:46, David Holmes wrote: > >> >> > >>>> Hi Daniil, > >> >> > >>>> > >> >> > >>>> On 2/10/2019 4:13 pm, Daniil Titov wrote: > >> >> > >>>>> Please review a change that fixes the issue. The > >> problem > >> >> here is > >> >> > >>>>> that that the thread is added to the ThreadIdTable > >> >> (introduced in > >> >> > >>>>> [3]) while the Threads_lock is held by > >> >> > >>>>> JVM_StartThread. When new thread is added to the > >> thread > >> >> table the > >> >> > >>>>> table checks if its load factor is greater than > >> required and > >> >> if so > >> >> > >>>>> it grows itself while polling for safepoints. > >> >> > >>>>> After changes [4] an attempt to block the thread while > >> >> holding the > >> >> > >>>>> Threads_lock results in assertion in > >> >> > >>>>> Thread::check_possible_safepoint(). > >> >> > >>>>> > >> >> > >>>>> The fix proposed by David Holmes ( thank you, > >> David!) is > >> >> to skip > >> >> > >>>>> the ThreadBlockInVM inside ThreadIdTable::grow() > >> method if the > >> >> > >>>>> current thread owns the Threads_lock. > >> >> > >>>> > >> >> > >>>> Sorry but looking at the fix in context now I think > >> it would be > >> >> > >>>> better to do this: > >> >> > >>>> > >> >> > >>>> while (gt.do_task(jt)) { > >> >> > >>>> if (Threads_lock->owner() == jt) { > >> >> > >>>> gt.pause(jt); > >> >> > >>>> ThreadBlockInVM tbivm(jt); > >> >> > >>>> gt.cont(jt); > >> >> > >>>> } > >> >> > >>>> } > >> >> > >>>> > >> >> > >>>> This way we don't waste time with the pause/cont when > >> there's no > >> >> > >>>> safepoint pause going to happen - and the owner() > >> check is > >> >> quicker > >> >> > >>>> than owned_by_self(). That partially addresses a general > >> >> concern I > >> >> > >>>> have about how long it may take to grow the table, as > >> we are > >> >> > >>>> deferring safepoints until it is complete in this > >> >> JVM_StartThread > >> >> > >>>> usecase. > >> >> > >>>> > >> >> > >>>> In the test you don't need all of: > >> >> > >>>> > >> >> > >>>> 32 * @run clean ThreadStartTest > >> >> > >>>> 33 * @run build ThreadStartTest > >> >> > >>>> 34 * @run main ThreadStartTest > >> >> > >>>> > >> >> > >>>> just the last @run suffices to build and run the test. > >> >> > >>>> > >> >> > >>>> Thanks, > >> >> > >>>> David > >> >> > >>>> ----- > >> >> > >>>> > >> >> > >>>>> Testing : Mach 5 tier1 and tier2 completed > >> successfully, > >> >> tier3 is > >> >> > >>>>> in progress. > >> >> > >>>>> > >> >> > >>>>> [1] Webrev: > >> >> http://cr.openjdk.java.net/~dtitov/8231666/webrev.01/ > >> >> > >>>>> [2] Bug: > >> https://bugs.openjdk.java.net/browse/JDK-8231666 > >> >> > >>>>> [3] https://bugs.openjdk.java.net/browse/JDK-8185005 > >> >> > >>>>> [4] https://bugs.openjdk.java.net/browse/JDK-8184732 > >> >> > >>>>> > >> >> > >>>>> Best regards, > >> >> > >>>>> Danill > >> >> > >>>>> > >> >> > >>>>> > >> >> > > >> >> > > >> >> > > >> >> > >> >> > >> > >> > > >