A White Paper on Neural Network Deployment
  • ❤️‍🔥A White Paper on Neural Network Deployment
    • ❤️‍🔥A White Paper on Neural Network Deployment
    • 🤠CUDA
      • 🤑CPU|GPU程序执行流程
      • 🤗QiuckLearnFromPicture
      • 🤒GPU编程模型
      • 🫣线程束和线程束分化|Warp
      • 🤭Reduction|并行规约
      • 🤔全局内存(Global Memory)访问模式
      • 🫢Share Memory|共享内存|Bank Conflicts
      • 😷CUDA流和事件
      • 🫡Nsight system和Nsight compute
      • 🤫Grid-Stride Loops
    • 😄ONNX
      • 😉ONNX中的各类Proto
      • 🤔onnx->torch
      • 🥳0x00自定义算子
      • 😕0x01自定义算子
      • 🥴ONNX 模型的修改与调试
      • 😆ONNX中的一些概念
      • 😍用python操作ONNX
      • 🥹ONNX中的广播机制
      • 🤣外部数据
      • 🥰ONNX Model hub
      • 😘ONNX IR(Intermediate Representation)
      • 🥳ONNX后端
      • 🥸概述
    • 🐶TensorRT
      • 🐱TensorRT快速入门指南
      • 🐭文档简介
      • 🐹TensorRT的功能
      • 🐰TensorRT的C++接口解析
      • 🦊TensorRT的Python接口解析
      • 🐻TensorRT如何工作
      • 🐼trtexec的使用
      • 🐻‍❄️实战:解析onnx模型保存为engine文件|from scratch
      • 🐨实战:加载engine文件并执行推理|from scratch
      • 🐯手撕TensoRT源码|0x00
    • 🫶模型量化和剪枝
      • 🖕IEEE754标准
      • 🫰浮点运算产生的误差
      • 🤲映射和偏移
      • 🫴quantization from scratch|python
      • 👏动态量化范围
      • 🤝量化粒度
      • 👍校准
      • 👊Post-Training Quantization
      • ✊Quantization-Aware Training
      • 🤞pytorch-quantization使用文档
      • ✌️Polygraphy-Cheatsheet
    • 🤺杂文不杂
      • 😾Roofline_model
      • 🤖模型部署的几大误区
      • 😽手算Ampere架构各个精度的Throughout
      • 😻Tensor Core VS CUDA Core
      • 😺PNNX计算图结构剖析
      • 🎃融合BN和Conv层
      • 👾深度神经网络编译器原理简介
      • 👽在WSL2上安装CUDA_cuDNN_TensorRT
    • 🍀CPP
      • 🪵lamda表达式|C++11
      • 🌴智能指针|C++11
      • 🌲右值引用|移动语义|完美转发|C++11
      • 🫑emplace_back 减少内存拷贝和移动|C++11
      • 🥬多线程|互斥锁|条件变量|C++11
      • 🥒异步操作|C++11
      • 🍆原子变量|CAS操作|内存顺序|C++11
      • 🍏对象生存期和资源管理|RAII设计思想
      • 🍎Pimpl设计模式|编译防火墙
      • 🌶️std::variant|C++17
      • 🫛std::any|C++17
    • 🩷部署实战
      • ❤️yolov8Multitask
      • 💚yolov5
      • 🧡pointpillars
      • 💛centerpoint
      • 🩵deepstream
      • 💜BEVfusion
      • 💙BEVLane
      • 🖤Occupancy
    • ☯️重点参考书籍
Powered by GitBook
On this page
  • PIMPL模式引入
  • Pimpl模式的好处
  • Pimpl模式的实现
  • Pimpl模式的详细解释
  • reference

Was this helpful?

Edit on GitHub
  1. A White Paper on Neural Network Deployment
  2. CPP

Pimpl设计模式|编译防火墙

Previous对象生存期和资源管理|RAII设计思想Nextstd::variant|C++17

Last updated 11 months ago

Was this helpful?

PIMPL模式引入

PIMPL模式引入 Pimpl(pointer to implementation,指向实现的指针)是一种常用的,用来对“类的接口与实现”进行解耦的方法。这个技巧可以避免在头文件中暴露私有细节,因此是促进API接口与实现保持完全分离的重要机制。但是Pimpl并不是严格意义上的设计模式(它是受制于C++特定限制的变通方案),这种惯用法可以看作桥接设计模式的一种特例。

Pimpl模式的好处

  1. 信息隐藏:通过将实现细节移到Impl类中,接口类的头文件不再暴露任何实现细节,从而实现了信息隐藏。

  2. 减少编译依赖:因为实现细节完全隐藏在实现文件中,接口头文件的更改不会导致依赖它的代码重新编译,从而减少了编译时间。

  3. 二进制兼容性:由于接口类的大小和布局保持不变,改动实现类的细节不会影响使用接口类的二进制文件,这有利于库的升级和维护。

  4. 更好的模块化:将实现细节与接口分离,可以更好地模块化代码,增强代码的可维护性和可读性。

Pimpl模式的实现

示例代码:未使用Pimpl模式

假设我们有一个Widget类,其实现如下:

// Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <string>
#include <vector>

class Widget {
public:
    Widget();
    ~Widget();

    void doSomething();
    void addValue(int value);

private:
    std::string name;
    std::vector<int> values;
};

#endif // WIDGET_H

// Widget.cpp
#include "Widget.h"
#include <iostream>

Widget::Widget() : name("default") {}

Widget::~Widget() {}

void Widget::doSomething() {
    std::cout << "Widget name: " << name << std::endl;
    for (int value : values) {
        std::cout << "Value: " << value << std::endl;
    }
}

void Widget::addValue(int value) {
    values.push_back(value);
}

在这个实现中,Widget类的实现细节(name和values成员)暴露在头文件中,导致头文件的更改会影响所有包含它的文件。

  • 头文件(.h或.hpp文件)通常包含类的声明、函数原型、宏定义等。头文件的变化会影响所有包含它的源文件,主要原因是:当一个源文件包含一个头文件时,编译器会将头文件的内容插入到源文件中。如果头文件中的内容发生变化,所有包含该头文件的源文件都需要重新编译,以确保生成的目标文件与新的头文件内容一致。

  • 源文件(.cpp或.cc文件)包含类或函数的具体实现。源文件的变化通常只需要重新编译自身,而不会影响其他源文件的编译,除非其他源文件依赖于修改后的源文件的编译结果。

示例代码:使用Pimpl模式

我们将改造Widget类,使其采用Pimpl模式:

// Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <memory>

class Widget {
public:
    Widget();
    ~Widget();

    void doSomething();
    void addValue(int value);

private:
    class Impl;                // 前向声明实现类
    std::unique_ptr<Impl> pImpl; // 使用unique_ptr管理实现类
};

#endif // WIDGET_H

// Widget.cpp
#include "Widget.h"
#include <iostream>
#include <string>
#include <vector>

// 实现类的定义
class Widget::Impl {
public:
    Impl() : name("default") {}
    ~Impl() {}

    void doSomething() {
        std::cout << "Widget name: " << name << std::endl;
        for (int value : values) {
            std::cout << "Value: " << value << std::endl;
        }
    }

    void addValue(int value) {
        values.push_back(value);
    }

private:
    std::string name;
    std::vector<int> values;
};

// Widget成员函数的定义
Widget::Widget() : pImpl(std::make_unique<Impl>()) {}

Widget::~Widget() = default;

void Widget::doSomething() {
    pImpl->doSomething();
}

void Widget::addValue(int value) {
    pImpl->addValue(value);
}

在这个改造后的版本中,我们将实现细节移到了Impl类中,接口类Widget仅持有一个指向实现类的指针。

Pimpl模式的详细解释

  • 在Widget的构造函数中,初始化pImpl,使用std::make_unique<Impl>()来创建实现类的实例。在析构函数中,使用default关键字,让编译器自动生成析构函数,以确保std::unique_ptr能够正确地销毁实现类的实例。

  • 在Widget的成员函数中,将所有操作委托给pImpl。例如,doSomething函数调用了pImpl->doSomething(),addValue函数调用了pImpl->addValue(value)。

reference

❤️‍🔥
🍀
🍎
https://learn.microsoft.com/en-us/cpp/cpp/?view=msvc-170
https://www.youtube.com/watch?v=lETcZQuKQBs
https://github.com/platisd/cpp-pimpl-tutorial