线程安全

Faiss的CPU索引在执行并行搜索时是线程安全的,其他的操作不能修改索引。如果多线程调用的了修改索引的函数,这时候需要开发人员实现互斥。

即使对于只读函数,Fais GPU索引也不是线程安全的。 这是因为GPU Faiss的StandardGpuResources不是线程安全的。 必须为搜索索引的每个线程创建一个单独StandardGpuResources对象。 多个GPU索引(多个索引,运行在相同GPU设备上)可以由单个CPU线程管理并共享相同的StandardGpuResources(实际应该是,因为它们可以共享GPU内存临时区域)。 单个GpuResources对象可以支持多个GPU设备,但只能支持单个调用CPU线程。 Multi-GPU Faiss (一个索引,使用的多个GPU设备,可以通过index_cpu_to_gpu_multiple获得)在内部运行来自不同线程的不同GPU索引。

注意:Faiss GPU实现,可以多个索引共享一个GPU设备,也可以一个索引使用多个GPU设备。

内部线程

Faiss本身有几种不同的内部线程。 对于CPU Faiss,索引(训练,添加,搜索)的三个基本操作是内部多线程的。 多线程是通过OpenMP和多线程BLAS实现完成的。 Faiss没有设置使用的线程数,开发人员可以通过设置环境变量OMP_NUM_THREADS调整此数字,也可以随时通过调用omp_set_num_threads(10)调整。faiss的Python接口包括了这个函数。

对于add和search操作,多线程在基于多个向量的, 这意味着search或add单个向量不是多线程的。

GPU版本的Faiss,在使用单个GPU时不是内部多CPU线程。

注意:faiis程序启动时,会创建n个线程(n为CPU核心数),如果每次search时都是单个向量,那只会使用一个线程,这个线程在search时会创建n-1个线程做并行,但是这n个线程都使用同一个cpu,所以计算资源固定,根本都不会加速,通过“export OMP_NUM_THREADS=1”可以设置只使用一个线程。

搜索性能

就QPS方面,最佳性能需要批量提交的。如果逐个提交查询,那么它们将在调用线程中执行(目前所有索引都是这种情况)。 因此,让多线程并行搜索也是相对加速方法。

但是如果多线程并行的批量查询是非常低效的,这将产生比cpu数量更多的线程。

注意:我在试验时,返现一个python程序会启动n个线程,在支持查询时只有一个线程会工作,而且这个线程还是创建n-1个子线程,数量远超过cpu数量了。

内部线程的性能(OpenML)

如何选择OpenMP的线程数量没有明确指标。 有一些架构设置少于cpu核心心的线程数,可以显着提高了执行效率。 例如,在Intel E5-2680 v2上,将线程数设置为20而不是默认值40是很有用的。

使用OpenBLAS的BLAS实现时,将线程策略设置为PASSIVE是很有用的

export OMP_WAIT_POLICY = PASSIVE

异步调用

执行搜索时并行做一些其他计算操作会很有用,包括:

  • 单线程计算
  • 等待I / O.
  • GPU计算

这些情况程序可以并行运行。对于Faiss CPU,与其他多线程计算(例如其他搜索)并行化是没有用的,因为这会产生太多线程并降低整体性能;在请求提交给Faiss索引之前,应该将来不同的请求数据做聚合处理,批量提交给Faiss处理。

在多个GPU上并行运行操作当然是可行且有用的,每个CPU线程专用于在使用不同GPU内核,这这个是有IndexProxy和IndexShards的实现方式。

如何生成搜索线程:

  • 在C ++中使用pthread_create + pthread_join
  • 在Python中使用 thread.start_new_thread +一个锁,或者使用multiprocessing.dummy.Pool。搜索,添加和训练功能会释放Global Interpreter Lock。