#1 is no big deal, we could just allocate one in a global class somewhere.<br><br>#2 actually seems quite desirable, is there any reason you don't want that?<br><br>#3 seems like a win for performance since no locks have to be acquired to manage the collection of threads<br><div class="gmail_quote"><div dir="ltr">On Sun, Apr 30, 2017 at 9:41 PM Scott Smith <<a href="mailto:scott.smith@purestorage.com">scott.smith@purestorage.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>The overall concept is similar; it comes down to implementation details like<br></div><div>1. llvm doesn't have a global pool, it's probably instantiated on demand<br></div><div>2. llvm keeps threads around until the pool is destroyed, rather than letting the threads exit when they have nothing to do<br></div><div>3. llvm starts up all the threads immediately, rather than on demand.<br><br></div><div>Overall I like the current lldb version better than the llvm version, but I haven't examined any of the use cases of the llvm version to know whether it could be dropped in without issue.  However, neither does what I want, so I'll move forward prototyping what I think it should do, and then see how applicable it is to llvm.<br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Apr 30, 2017 at 9:02 PM, Zachary Turner <span dir="ltr"><<a href="mailto:zturner@google.com" target="_blank">zturner@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Have we examined llvm::ThreadPool to see if it can work for our needs?  And if not, what kind of changes would be needed to llvm::ThreadPool to make it suitable?</div><br><div class="gmail_quote"><div><div class="m_-5268301279702299042h5"><div dir="ltr">On Fri, Apr 28, 2017 at 8:04 AM Scott Smith via lldb-dev <<a href="mailto:lldb-dev@lists.llvm.org" target="_blank">lldb-dev@lists.llvm.org</a>> wrote:<br></div></div></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="m_-5268301279702299042h5"><div dir="ltr"><div><div><div>Hmmm ok, I don't like hard coding pools.  Your idea about limiting the number of high level threads gave me an idea:<br><br></div>1. System has one high level TaskPool.  <br>2. TaskPools have up to one child and one parent (the parent for the high level TaskPool = nullptr).<br>3. When a worker starts up for a given TaskPool, it ensures a single child exists.<br>4. There is a thread local variable that indicates which TaskPool that thread enqueues into (via AddTask).  If that variable is nullptr, then it is the high level TaskPool.Threads that are not workers enqueue into this TaskPool.  If the thread is a worker thread, then the variable points to the worker's child.<br></div><div>5. When creating a thread in a TaskPool, it's thread count AND the thread count of the parent, grandparent, etc are incremented.<br></div><div>6. In the main worker loop, if there is no more work to do, OR the thread count is too high, the worker "promotes" itself.  Promotion means:<br>a. decrement the thread count for the current task pool<br>b. if there is no parent, exit; otherwise, become a worker for the parent task pool (and update the thread local TaskPool enqueue pointer).<br><br></div><div>The main points are:<br></div><div>1. We don't hard code the number of task pools; the code automatically uses the fewest number of taskpools needed regardless of the number of places in the code that want task pools.<br></div><div>2. When the child taskpools are busy, parent taskpools reduce their number of workers over time to reduce oversubscription.<br><br></div><div>You can fiddle with the # of allowed threads per level; for example, if you take into account number the height of the pool, and the number of child threads, then you could allocate each level 1/2 of the number of threads as the level below it, unless the level below wasn't using all the threads; then the steady state would be 2 * cores, rather than height * cores.  I think that it probably overkill though.<br></div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Apr 28, 2017 at 4:37 AM, Pavel Labath <span dir="ltr"><<a href="mailto:labath@google.com" target="_blank">labath@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On 27 April 2017 at 00:12, Scott Smith via lldb-dev<br>
<span><<a href="mailto:lldb-dev@lists.llvm.org" target="_blank">lldb-dev@lists.llvm.org</a>> wrote:<br>
> After a dealing with a bunch of microoptimizations, I'm back to<br>
> parallelizing loading of shared modules.  My naive approach was to just<br>
> create a new thread per shared library.  I have a feeling some users may not<br>
> like that; I think I read an email from someone who has thousands of shared<br>
> libraries.  That's a lot of threads :-)<br>
><br>
> The problem is loading a shared library can cause downstream parallelization<br>
> through TaskPool.  I can't then also have the loading of a shared library<br>
> itself go through TaskPool, as that could cause a deadlock - if all the<br>
> worker threads are waiting on work that TaskPool needs to run on a worker<br>
> thread.... then nothing will happen.<br>
><br>
> Three possible solutions:<br>
><br>
> 1. Remove the notion of a single global TaskPool, but instead have a static<br>
> pool at each callsite that wants it.  That way multiple paths into the same<br>
> code would share the same pool, but different places in the code would have<br>
> their own pool.<br>
><br>
<br>
</span>I looked at this option in the past and this was my preferred<br>
solution. My suggestion would be to have two task pools. One for<br>
low-level parallelism, which spawns<br>
std::thread::hardware_concurrency() threads, and another one for<br>
higher level tasks, which can only spawn a smaller number of threads<br>
(the algorithm for the exact number TBD). The high-level threads can<br>
access to low-level ones, but not the other way around, which<br>
guarantees progress.<br>
<br>
I propose to hardcode 2 pools, as I don't want to make it easy for<br>
people to create additional ones -- I think we should be having this<br>
discussion every time someone tries to add one, and have a very good<br>
justification for it (FWIW, I think your justification is good in this<br>
case, and I am grateful that you are pursuing this).<br>
<span class="m_-5268301279702299042m_7784186648115103507m_3806517130016788247HOEnZb"><font color="#888888"><br>
pl<br>
</font></span></blockquote></div><br></div></div></div><span>
_______________________________________________<br>
lldb-dev mailing list<br>
<a href="mailto:lldb-dev@lists.llvm.org" target="_blank">lldb-dev@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-dev" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-dev</a><br>
</span></blockquote></div>
</blockquote></div><br></div>
</blockquote></div>