【问题标题】:Mulithreading Usage多线程使用
【发布时间】:2018-07-28 05:51:22
【问题描述】:

我正在迭代一个包含 +- 2000 万个条目的 HashMap。在每次迭代中,我都会再次使用 +- 2000 万个条目迭代 HashMap。

 HashMap<String, BitSet> data_1 = new HashMap<String, BitSet>
HashMap<String, BitSet> data_2 = new HashMap<String, BitSet>

我根据线程数将 data_1 分成块(线程 = 核心,我有四个核心处理器)。

我的代码执行时间超过 20 小时。排除不将结果存储到文件中。

1) 如果我想将每个线程的结果存储而不重叠到文件中,我该如何 这样做?

2) 我怎样才能使以下速度更快。

3) 如何根据核心数动态创建块?

  int cores = Runtime.getRuntime().availableProcessors();
  int threads = cores;

  //Number of threads
  int Chunks = data_1.size() / threads;


      //I don't trust with chunks created by the below line, that's why i created chunk1, chunk2, chunk3, chunk4 seperately and validated them.
      Map<Integer, BitSet>[] Chunk= (Map<Integer, BitSet>[]) new HashMap<?,?>[threads];

4) 如何使用 for 循环创建线程?我在做什么正确吗?

ClassName thread1 = new ClassName(data2, chunk1);
ClassName thread2 = new ClassName(data2, chunk2);
ClassName thread3 = new ClassName(data2, chunk3);
ClassName thread4 = new ClassName(data2, chunk4);

 thread1.start();
 thread2.start();
 thread3.start();
 thread4.start();

 thread1.join();
 thread2.join();
 thread3.join();
 thread4.join();

我的代码的表示

Public class ClassName {
Integer nSimilarEntities = 30;

    public void run() {


            for (String kNonRepeater : data_1.keySet()) {

                    // Extract the feature vector
                      BitSet vFeaturesNonRepeater = data_1.get(kNonRepeater);


                    // Calculate the sum of 1s (L2 norm is the sqrt of this)
                    double nNormNonRepeater = Math.sqrt(vFeaturesNonRepeater.cardinality());

            // Loop through the repeater set
                    double nMinSimilarity = 100;
                    int nMinSimIndex = 0;

                    // Maintain the list of top similar repeaters and the similarity values


                    long dpind = 0;
                    ArrayList<String> vSimilarKeys = new ArrayList<String>();
                    ArrayList<Double> vSimilarValues = new ArrayList<Double>();

                    for (String kRepeater : data_2.keySet()) {
                        // Status output at regular intervals
                        dpind++;
                        if (Math.floorMod(dpind, pct) == 0) {
                            System.out.println(dpind + " dot products (" + Math.round(dpind / pct) + "%) out of "
                                    + nNumSimilaritiesToCompute + " completed!");
                        }

                        // Calculate the norm of repeater, and the dot product

                        BitSet vFeaturesRepeater = data_2.get(kRepeater);


                        double nNormRepeater = Math.sqrt(vFeaturesRepeater.cardinality());
                        BitSet vTemp = (BitSet) vFeaturesNonRepeater.clone();
                        vTemp.and(vFeaturesRepeater);
                        double nCosineDistance = vTemp.cardinality() / (nNormNonRepeater * nNormRepeater);



                    //  queue.add(new MyClass(kRepeater,kNonRepeater,nCosineDistance));

                    //  if(queue.size() > YOUR_LIMIT)
                    //          queue.remove();

                        // Don't bother if the similarity is 0, obviously
                        if ((vSimilarKeys.size() < nSimilarEntities) && (nCosineDistance > 0)) {

                            vSimilarKeys.add(kRepeater);
                            vSimilarValues.add(nCosineDistance);

                            nMinSimilarity = vSimilarValues.get(0);
                            nMinSimIndex = 0;
                            for (int j = 0; j < vSimilarValues.size(); j++) {
                                if (vSimilarValues.get(j) < nMinSimilarity) {
                                    nMinSimilarity = vSimilarValues.get(j);
                                    nMinSimIndex = j;
                                }
                            }
                        } else { // If there are more, keep only the best
                            // If this is better than the smallest distance, then remove the smallest
                            if (nCosineDistance > nMinSimilarity) {
                                // Remove the lowest similarity value
                                vSimilarKeys.remove(nMinSimIndex);
                                vSimilarValues.remove(nMinSimIndex);
                                // Add this one
                                vSimilarKeys.add(kRepeater);
                                vSimilarValues.add(nCosineDistance);
                                // Refresh the index of lowest similarity value
                                nMinSimilarity = vSimilarValues.get(0);
                                nMinSimIndex = 0;
                                for (int j = 0; j < vSimilarValues.size(); j++) {
                                    if (vSimilarValues.get(j) < nMinSimilarity) {
                                        nMinSimilarity = vSimilarValues.get(j);
                                        nMinSimIndex = j;
                                    }
                                }
                            }
                        } // End loop for maintaining list of similar entries

                    }// End iteration through repeaters

            for (int i = 0; i < vSimilarValues.size(); i++) {
                    System.out.println(Thread.currentThread().getName() + kNonRepeater + "|" + vSimilarKeys.get(i) + "|" + vSimilarValues.get(i));
          }
       }
   }
}

最后,如果不是多线程,java中是否还有其他方法可以降低时间复杂度。

【问题讨论】:

  • 向量的类型有哪些?
  • 你使用的算法是什么?
  • @Duke,代码已更新。
  • 使用BigDecimal 意味着程序必须对每个数字进行计算 - 更多数字 -> 更多计算 -> 更多时间....没有太大区别,就像您必须手动计算一样纸
  • @VishwanathGulabal 你用什么java版本,你可以用什么java版本?

标签: java time-complexity


【解决方案1】:

计算机的工作方式与您必须手动完成的工作类似(它一次处理更多的数字/位,但问题是相同的。

如果你做加法,时间与数字的大小成正比。

如果你做乘法或除数,它与数字大小的平方成正比。

对于计算机,大小基于 32 或 64 个有效位的倍数,具体取决于实现。

【讨论】:

  • 你能看看我更新的原始问题并给出一些克服时间复杂度的方法吗?
【解决方案2】:

我会说这个任务适合并行流。如果您有时间,请不要犹豫,看看这个概念。并行流无缝地全速使用多线程。

顶层处理如下所示:

data_1.entrySet()
      .parallelStream()
      .flatmap(nonRepeaterEntry -> processOne(nonRepeaterEntry.getKey(), nonRepeaterEntry.getValue(), data2))
      .forEach(System.out::println);

你应该为 processOne 函数提供这样的原型:

Stream<String> processOne(String nonRepeaterKey, String nonRepeaterBitSet, Map<String BitSet> data2);

它将返回准备好的字符串列表,其中包含您现在打印到文件中的内容。

要在里面做流,你可以先准备List列表,然后在return语句中把它变成流:

return list.stream();

尽管可以在流中处理内部循环,但不鼓励使用并行流 - 您已经有足够的并行度。

对于您的问题:

1) 如果我想将每个线程的结果存储到文件中而不重叠,我该怎么做?

任何日志框架(logback、log4j)都可以处理它。并行流可以处理它。您也可以将准备好的行存储到一些队列/数组中,并在单独的线程中打印它们。不过,这需要一点小心,现成的解决方案更容易、更有效地做到这一点。

2) 我怎样才能使以下速度更快。

优化和并行化。在正常情况下,你得到 number_of_threads/1.5..number_of_threads 倍的处理速度,认为你有超线程在起作用,但这取决于你不那么并行的事情和东西的底层实现。

3) 如何根据核心数动态创建块?

您不必这样做。制作一个任务列表(每个 data_1 条目 1 个任务)并用它们提供执行器服务 - 这已经足够大的任务大小了。您可以使用以线程数为参数的 FixedThreadPool ,它会平均分配任务。

您不应该创建任务类,在 threadpool.submit 上为每个任务获取 Future,最后运行一个循环,为每个 Future 执行 .get。它会将主线程降低到执行器处理速度,隐式地执行类似 fork-join 的行为。

4) 直接创建线程是过时的技术。建议使用某种执行器服务,并行流等。对于循环处理,您需要创建块列表,并在循环中创建线程,将其添加到线程列表中。如果列表,则在另一个循环中加入每个线程。


临时优化:

1) 创建Repeater 类来存储key、bitset 和cardinality。预处理您的哈希集,将它们转换为中继器实例并计算一次基数(即不是针对每个内部循环运行)。它将为您节省 2000 万*(2000 万-1)次 .cardinality() 调用。您仍然需要称其为差异。

2) 在组合条目上用大小有限的 priorityQueue 替换similarKeys、similarValues。对于 30 个元素,它的运行速度更快。

查看此问题以了解有关 PriorityQueue 的信息: Java PriorityQueue with fixed size

3) 如果 nonRepeater 的基数已经为 0 - bitSet 并且永远不会增加结果基数,则您可以跳过处理 nonRepeater,并且您将过滤掉所有 0 距离值。

4) 您可以跳过(从您在 p.1 优化中创建的临时列表中删除)每个具有零基数的中继器。就像在第 3 页中一样,它永远不会产生任何成果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2021-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多