Tensorflow XLA编译优化(在线推理)

XLA编译优化可以有CPU版本也可以GPU版本,tensorflow特定的接口对机器上的device进行管理,作为推理C++ Serving服务端需要增加资源管理的模块功能;

1、资源管理

1、gpu卡的管理

(1)获取gpu卡数量  cudaGetDeviceCount(&gpu_count);

2、封装了一个executor调度器

可以通过下面配置打开,options.enable_internal_executor = true;

主要代码是在executor.cc, 主要实现了当推理消息收到后检测stream状态调度更加均匀,默认的情况随机选择一个stream,然后使用一个锁保证一个线程在调用stream,这样可能存在等待问题;

实际压测发现有效果,基本可以跟tensorrt性能达到一致;

3、设备上下文stream_executor

StatusOr Backend::stream_executor 

stream_executor 是表示设备上下文的指针。在这个特定的代码实现中,se::StreamExecutor* 是基于 StreamExecutor, 它是一个 device 环境的一个抽象类,用于管理 device 上下文以及分配 device 的资源。 Backend 类装饰了StreamExecutor 并提供了管理装备的外部接口的特定实现。 通过这个 StreamExecutor 对象,Backend 类可以执行任何与设备相关的任务,包括在设备上分配和拷贝内存、执行计算任务和与设备通信等等。

4. 获取device内存大小

stream_exec->DeviceMemoryUsage(&available_memory, &total_memory))

5. GPU上的内存管理BFCAllocator

需要使用一个内存分配器来管理设备的内存使用情况。BFCAllocator 是一种在设备上使用的内存分配器,该分配器使用分配器中的 Largest Best Fit (LBF) 算法来自动管理设备上的内存分配和释放。

LBF 算法是一种高效的内存分配算法,它可以避免内存碎片和帮助最大化设备内存的利用率。BFCAllocator 通过实现自己的版本来使用这种算法来管理设备内存,可以在 GPU 和 CNN 等计算密集型任务中提高性能。通过封装 Tensorflow 的设备内存分配器 API,使用 BFCAllocator 类可以更方便地实现设备内存管理,减少了内存分配和释放时的开销。

tensorflow::DeviceMemAllocator 和 tensorflow::BFCAllocator 都是 TensorFlow 中的分配器,用于管理 GPU 设备上的内存分配和释放。

tensorflow::DeviceMemAllocator 是一种简单的分配器,用于在 TensorFLow 中管理单个设备上的内存分配和释放。它基于 CUDA API 实现,在每个设备上维护一个内存池,并支持对内存池的透明管理。DeviceMemAllocator 没有跨设备管理和碎片整理等高级功能,但可以满足一般的内存管理需求。

tensorflow::BFCAllocator 是一种更高级的分配器,它是分布式的分块填充(block-fragmented caching,BFC)分配器,用于管理 TensorFlow 中的 GPU 设备上的内存申请和释放。相比于 DeviceMemAllocatorBFCAllocator 的特点是可以避免内存碎片,提高设备内存的使用效率。BFCAllocator 也具有跨设备管理的功能,它可以根据预定义的算法在不同设备之间分配内存,以满足需求。

总的来说,tensorflow::DeviceMemAllocator 主要用于简单的单设备内存管理,而 tensorflow::BFCAllocator 主要用于更复杂的分布式设备内存管理和优化,但它们可以共同工作,BFCAllocator 可以使用 DeviceMemAllocator 作为其基础设施来实现更高级的内存功能和优化。

6.local_client句柄通过GetOrCreateLocalClient

获得local_client后,可以通过其管理整个gpu设备,多个设备device的管理

xla::Backend* backend = local_client->mutable_backend()

auto stream_executor =  backend->stream_executor(device_ordinal).ValueOrDie();

//device_ordinal gpu设备序号

7、XLA中的Buckets

这个是多少个分片作为一个桶,推理的时候是按照固定的分片划分几个范围,每个桶申请gpu内存

8、xla compile和executable_buckets

每个桶独立编译一个executable  通过device_->compile最底层调用的还是

local_client_->Compile(computation, shapes, build_options);返回值包含是xla::LocalExecutable

最终执行推理的句柄是std::unique_ptr这种类型的

9、Local_executable和Stream关联

编译后的可执行程序xla::LocalExecutable 执行RunAsync进行推理,

RunAsync()函数推理的参数中有tf_stream参数,使用那个流进行推理,其中流外面加锁了,保证只有一个线程执行该stream,默认是随随机选择一个流执行,可能存在等待问题

gpu_executable->local_executable->RunAsync()

10、RunAsync推理

std::unique_ptr local_executable;

local_executable->RunAsync()

11、Stream流的线程安全问题

    stream_executor 中的 Stream 类并不支持多线程操作。它是一个抽象基类,用于表示异步执行的操作流。实现 Stream 的具体的子类可以支持不同的异构计算加速器(如 GPU、TPU、FPGA 等),并且可以并行地执行多个操作。但是,单个 Stream 对象的操作是串行的,即它们是以 FIFO 的方式在同一队列中运行的,而且这些操作的执行顺序是不可预测的。如果要实现多线程操作,需要单独创建多个 Stream 对象,然后在多个线程中向这些 Stream 对象提交操作。这些 Stream 对象可以在不同的设备上执行操作,或者在同一个设备上使用不同的计算资源(如 CUDA 核心或张量核心)执行操作。需要注意的是,在实际操作中,多线程的并行操作可能会产生与单线程不同的结果,因此需要注意调试和测试。

12、stream和GPU设备的关系

一个GPU卡可以设置4个stream对象,其实stream也可以用在不同gpu设备上,但是值得注意的是,应该避免在不同 GPU 之间频繁地切换 Stream 对象,因为这会导致数据传输和同步开销,从而降低计算效率。

你可能感兴趣的:(tensorflow,人工智能)