scott.smith created this revision.

Many parallel tasks just want to iterate over all the possible numbers from 0 
to N-1.  Rather than enqueue N work items, instead just "map" the function 
across the requested integer space.


Repository:
  rL LLVM

https://reviews.llvm.org/D32757

Files:
  include/lldb/Utility/TaskPool.h
  source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
  source/Utility/TaskPool.cpp
  unittests/Utility/TaskPoolTest.cpp

Index: unittests/Utility/TaskPoolTest.cpp
===================================================================
--- unittests/Utility/TaskPoolTest.cpp
+++ unittests/Utility/TaskPoolTest.cpp
@@ -52,3 +52,15 @@
 
   ASSERT_EQ(4, count);
 }
+
+TEST(TaskPoolTest, TaskMap) {
+  int data[4];
+  auto fn = [&data](int x) { data[x] = x * x; };
+
+  TaskMap(4, 1, fn);
+
+  ASSERT_EQ(data[0] == 0);
+  ASSERT_EQ(data[1] == 1);
+  ASSERT_EQ(data[2] == 4);
+  ASSERT_EQ(data[3] == 9);
+}
Index: source/Utility/TaskPool.cpp
===================================================================
--- source/Utility/TaskPool.cpp
+++ source/Utility/TaskPool.cpp
@@ -73,3 +73,29 @@
     f();
   }
 }
+
+void TaskMap(size_t size, size_t batch, std::function<void(size_t)> const & func)
+{
+  std::atomic<size_t> idx{0};
+  size_t est_batches = std::min<size_t>((size + batch - 1) / batch,
+                                        std::thread::hardware_concurrency());
+
+  auto wrapper = [&idx, size, batch, &func]()
+  {
+    while (true) {
+      size_t batch_start = idx.fetch_add(batch);
+      if (batch_start >= size)
+        break;
+      size_t batch_end = std::min(batch_start + batch, size);
+      for (size_t i = batch_start; i < batch_end; i++)
+        func(i);
+    }
+  };
+
+  std::vector<std::future<void>> futures;
+  futures.reserve(est_batches);
+  for (size_t i = 0; i < est_batches; i++)
+    futures.push_back(TaskPool::AddTask(wrapper));
+  for (size_t i = 0; i < est_batches; i++)
+    futures[i].wait();
+}
Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
===================================================================
--- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -1946,7 +1946,8 @@
     std::vector<NameToDIE> type_index(num_compile_units);
     std::vector<NameToDIE> namespace_index(num_compile_units);
 
-    std::vector<bool> clear_cu_dies(num_compile_units, false);
+    // std::vector<bool> might be implemented using bit test-and-set, so use uint8_t instead.
+    std::vector<uint8_t> clear_cu_dies(num_compile_units, false);
     auto parser_fn = [debug_info, &function_basename_index,
                       &function_fullname_index, &function_method_index,
                       &function_selector_index, &objc_class_selectors_index,
@@ -1963,22 +1964,18 @@
       return cu_idx;
     };
 
-    auto extract_fn = [debug_info](uint32_t cu_idx) {
+    auto extract_fn = [debug_info, &clear_cu_dies](uint32_t cu_idx) {
       DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx);
       if (dwarf_cu) {
         // dwarf_cu->ExtractDIEsIfNeeded(false) will return zero if the
         // DIEs for a compile unit have already been parsed.
-        return std::make_pair(cu_idx, dwarf_cu->ExtractDIEsIfNeeded(false) > 1);
+        if (dwarf_cu->ExtractDIEsIfNeeded(false) > 1)
+          clear_cu_dies[cu_idx] = true;
       }
-      return std::make_pair(cu_idx, false);
     };
 
     // Create a task runner that extracts dies for each DWARF compile unit in a
     // separate thread
-    TaskRunner<std::pair<uint32_t, bool>> task_runner_extract;
-    for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx)
-      task_runner_extract.AddTask(extract_fn, cu_idx);
-
     //----------------------------------------------------------------------
     // First figure out which compile units didn't have their DIEs already
     // parsed and remember this.  If no DIEs were parsed prior to this index
@@ -1988,30 +1985,15 @@
     // a DIE in one compile unit refers to another and the indexes accesses
     // those DIEs.
     //----------------------------------------------------------------------
-    while (true) {
-      auto f = task_runner_extract.WaitForNextCompletedTask();
-      if (!f.valid())
-        break;
-      unsigned cu_idx;
-      bool clear;
-      std::tie(cu_idx, clear) = f.get();
-      clear_cu_dies[cu_idx] = clear;
-    }
+    TaskMap(num_compile_units, 1, extract_fn);
 
     // Now create a task runner that can index each DWARF compile unit in a
     // separate
     // thread so we can index quickly.
 
-    TaskRunner<uint32_t> task_runner;
-    for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx)
-      task_runner.AddTask(parser_fn, cu_idx);
-
-    while (true) {
-      std::future<uint32_t> f = task_runner.WaitForNextCompletedTask();
-      if (!f.valid())
-        break;
-      uint32_t cu_idx = f.get();
+    TaskMap(num_compile_units, 1, parser_fn);
 
+    for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) {
       m_function_basename_index.Append(function_basename_index[cu_idx]);
       m_function_fullname_index.Append(function_fullname_index[cu_idx]);
       m_function_method_index.Append(function_method_index[cu_idx]);
Index: include/lldb/Utility/TaskPool.h
===================================================================
--- include/lldb/Utility/TaskPool.h
+++ include/lldb/Utility/TaskPool.h
@@ -186,4 +186,11 @@
     ;
 }
 
+// Run 'func' on every value from 0 .. size-1.  Each worker will grab 'batch'
+// numbers at a time to work on, so for very fast functions, batch should be
+// large enough to avoid too much cache line contention.
+void TaskMap(size_t size, size_t batch,
+             std::function<void(size_t)> const & func);
+
+
 #endif // #ifndef utility_TaskPool_h_
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to