java - 如何提高Java多线程性能?从 NoSQL 数据库(如 Redis)与 ArrayList 保存/加载数据的时间效率?

标签 java multithreading performance arraylist

我正在评估一个 SDK,我需要交叉比较存储在图库文件夹中的约 15000 张虹膜图像,并将相似度分数生成为 15000 x 15000 矩阵。

所以我对所有图像进行了预处理,并将处理后的 blob 存储在 ArrayList 中。然后我在 run 方法中使用带有 2 个“for”循环的多个线程来调用“比较”方法(来自 SDK)并将 ArrayList 的索引作为参数传递以比较这些各自的 blob 并将整数返回值保存在使用 Apache poi 库的 excel 表。 性能非常低效(每次比较需要约 40 毫秒),整个任务需要花费大量时间(估计约 100 天,8 核以 100% 运行)来完成所有 225,000,000 次比较。请帮助我理解这个瓶颈。

Multithreading code

int processors = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(processors); 
for(int i =0; i<processors; i++) { 
  //each thread compares 1875 images with 15000 images
  Runnable task = new Thread(bloblist,i*1875,i*1875+1874); 
  executor.execute(task);
}   
executor.shutdown();

Run Method

public void run(){
 for(int i = startIndex; i<= lastIndex; i++) {
    for(int j=0;j<15000;j++){
        compare.compareIris(bloblist.get(i),bloblist.get(j));
        score= compare.getScore();
        //save result to Excel using Apache POI
        ...
        ...
        }
 }
}

请建议我一个高效的架构来完成这项任务。我应该将 blob 存储在 NoSQL 数据库中还是有任何替代方法来执行此操作?

最佳答案

作为第一步,我会考虑向您的代码添加一些简单的分析。分析库很棒,但可能有点令人生畏。您真正需要开始的是:

public void run(){
 long sumCompare = 0;
 long sumSave = 0
 for(int i = startIndex; i<= lastIndex; i++) {
    for(int j=0;j<15000;j++){
        final long compareStart = System.currentTimeMillis();
        compare.compareIris(bloblist.get(i),bloblist.get(j));
        score= compare.getScore();
        final long compareEnd = System.currentTimeMillis();
        compareSum += (compareEnd - compareStart);
        //save result to Excel using Apache POI
        ...
        ...
        final long saveEnd = System.currentTimeMillis();
        saveSum += (saveEnd - compareEnd);
        }
 }
System.out.println(String.format("Compare: %d; Save: %d", sumCompare, sumSave);
}

也许可以在 100x100 的网格上运行它,以大致了解大部分运行时的位置。

如果是保存步骤,我强烈建议使用数据库作为计算分数和将其导出到电子表格之间的中间步骤。 NoSQL 数据库可以工作,尽管为了简单起见,我也鼓励您查看 SQLite 之类的东西。 (许多 NoSQL 数据库旨在提供跨数据库节点集群的优势,同时处理非常大的数据集;如果您在一个节点上存储只写数据,SQL 可能是您的最佳选择。)

如果瓶颈在计算环节,性能提升会比较困难。如果 blob 不能完全适合 RAM 以及比较消耗的任何 RAM,您可能要付出将这些数据交换到磁盘上和从磁盘上交换的代价。通过让每个线程“脱离队列”而不是从预先分配的 block 开始,您可能会看到一个小的改进:

final int processors = Runtime.getRuntime().availableProcessors();
final ExecutorService executor = Executors.newFixedThreadPool(processors); 
final AtomicLong nextCompare = new AtomicLong(0);

for(int i =0; i<processors; i++) { 
  Runnable task = new Thread(bloblist, nextCompare); 
  executor.execute(task);
}   
executor.shutdown();

public void run(){
  while (true) {
    final long taskNum = nextCompare.getAndIncrement();
    if (taskNum >= 15000 * 15000) {
      return;
    }
    final long i = Math.floor(taskNum/15000);
    final long j = taskNum % 15000;
    compare.compareIris(bloblist.get(i),bloblist.get(j));
    score = compare.getScore();
    // Save score, etc.)
  }
}

这将导致所有线程在内存中相对靠近地存储的 blob 上工作。这样,没有线程正在从缓存中逐出另一个线程在不久的将来需要的数据。但是,您要付出锁定 AtomicLong 的代价;如果内存抖动不是您的问题,这可能会慢一点。

关于java - 如何提高Java多线程性能?从 NoSQL 数据库(如 Redis)与 ArrayList 保存/加载数据的时间效率?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45288349/

相关文章:

java - 使用 java rx.Observable 进行并行 API 调用

JavaFX-获取网格 Pane 上的点击位置

java - 最终网页后运行java程序

java - 使用 Linux 为 Java 应用程序设置最大创建线程数

c++ - 最多 3 个值,左关联版本与右关联版本的性能

c++ - 如何使用包含大量数据的类元素处理 STL 容器

java - GridView 重复一张图像

java - 无法在原始类型 void 上调用 forEach((<no type> de) -> {})

C++ 术语不会计算为采用 0 个参数线程套接字的函数

c# - SerialPort RS-485 和通信限制