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
  • 引出RAII
  • RAII 的好处
  • 一些自定义的RAII类
  • reference

Was this helpful?

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

对象生存期和资源管理|RAII设计思想

引出RAII

与Python不同,C++ 没有自动回收垃圾,这是在程序运行时释放堆内存和其他资源的 一个内部进程。 C++ 程序负责将所有已获取的资源返回到操作系统。 未能释放未使用的 资源称为“泄漏”。 在进程退出之前,泄漏的资源无法用于其他程序。 特别是内存泄漏是 C / C++编程中 bug 的常见原因。

新式 C++ 通过声明栈上的对象,尽可能避免使用堆内存。 当某个资源对于栈来说太 大时,则它应由对象拥有。 当该对象初始化时,它会获取它拥有的资源。 然后,该对象 负责在其析构函数中释放资源。 在栈上声明拥有资源的对象本身。 对象拥有资源的 则也称为“资源获取即初始化”(RAII)。

当拥有资源的栈对象超出范围时,会自动调用其析构函数。 这样,C++ 中的垃圾回收 与对象生存期密切相关,是确定性的。 资源始终在程序中的已知点发布,你可以控制该 点。 仅类似 C++ 中的确定析构函数可公平处理内存和非内存资源。

RAII 的好处

  1. 自动资源管理:资源的获取和释放绑定在对象的构造与析构中,减少了手动管理资源的负担。

  2. 异常安全:在异常发生时,析构函数会自动调用,确保资源得到正确释放。

  3. 简化代码:减少显式的资源管理代码,使代码更简洁、更容易维护。

  4. 提升代码可读性:资源管理逻辑清晰地嵌入到对象的生命周期中,提高代码的可读性。

一些自定义的RAII类

1. 内存管理

虽然智能指针已经很好的解决了内存管理的问题,但在一些特定的场景中,我们可能需要自定义内存管理类。

示例:自定义内存块管理类

#include <iostream>
#include <cstring>

class MemoryBlock {
public:
    MemoryBlock(size_t size) {
        data_ = new char[size]; // 分配内存
        size_ = size;
        std::cout << "MemoryBlock of size " << size << " allocated." << std::endl;
    }

    ~MemoryBlock() {
        delete[] data_; // 释放内存
        std::cout << "MemoryBlock of size " << size_ << " deallocated." << std::endl;
    }

    // 禁止拷贝
    MemoryBlock(const MemoryBlock&) = delete;
    MemoryBlock& operator=(const MemoryBlock&) = delete;

    // 允许移动
    MemoryBlock(MemoryBlock&& other) noexcept : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
    }

    MemoryBlock& operator=(MemoryBlock&& other) noexcept {
        if (this != &other) {
            delete[] data_;
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
        }
        return *this;
    }

    char* data() { return data_; }
    size_t size() const { return size_; }

private:
    char* data_;
    size_t size_;
};

int main() {
    MemoryBlock block1(1024); // 分配1KB的内存块
    MemoryBlock block2(2048); // 分配2KB的内存块

    // 将block1的资源转移到block3
    MemoryBlock block3(std::move(block1));

    return 0;
}

2. 线程锁管理

在多线程编程中,使用RAII管理锁可以确保在所有代码路径上都能正确释放锁,避免死锁的发生。

示例:自定义锁管理类

#include <iostream>
#include <mutex>

class LockGuard {
public:
    explicit LockGuard(std::mutex& m) : mutex_(m) {
        mutex_.lock(); // 获取锁
        std::cout << "Lock acquired." << std::endl;
    }

    ~LockGuard() {
        mutex_.unlock(); // 释放锁
        std::cout << "Lock released." << std::endl;
    }

    // 禁止拷贝和赋值
    LockGuard(const LockGuard&) = delete;
    LockGuard& operator=(const LockGuard&) = delete;

private:
    std::mutex& mutex_;
};

int main() {
    std::mutex mtx;

    {
        LockGuard lock(mtx); // 自动获取和释放锁
        std::cout << "Critical section." << std::endl;
    } // 离开作用域时自动释放锁

    return 0;
}

3. 文件描述符管理

管理文件描述符或套接字等资源,确保在所有代码路径上都能正确关闭资源。

示例:自定义文件描述符管理类

#include <iostream>
#include <unistd.h> // for close()

class FileDescriptor {
public:
    explicit FileDescriptor(int fd) : fd_(fd) {
        std::cout << "FileDescriptor " << fd << " acquired." << std::endl;
    }

    ~FileDescriptor() {
        if (fd_ != -1) {
            close(fd_); // 关闭文件描述符
            std::cout << "FileDescriptor " << fd_ << " released." << std::endl;
        }
    }

    // 禁止拷贝
    FileDescriptor(const FileDescriptor&) = delete;
    FileDescriptor& operator=(const FileDescriptor&) = delete;

    // 允许移动
    FileDescriptor(FileDescriptor&& other) noexcept : fd_(other.fd_) {
        other.fd_ = -1;
    }

    FileDescriptor& operator=(FileDescriptor&& other) noexcept {
        if (this != &other) {
            if (fd_ != -1) {
                close(fd_);
            }
            fd_ = other.fd_;
            other.fd_ = -1;
        }
        return *this;
    }

    int get() const { return fd_; }

private:
    int fd_;
};

int main() {
    int fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        std::cerr << "Failed to open file." << std::endl;
        return 1;
    }

    FileDescriptor file(fd); // 自动管理文件描述符

    // 使用文件描述符进行操作

    return 0;
}

4. 网络资源管理

在网络编程中,管理套接字的生命周期,确保在所有代码路径上都能正确关闭套接字。

示例:自定义套接字管理类

#include <iostream>
#include <sys/socket.h>
#include <unistd.h> // for close()

class Socket {
public:
    explicit Socket(int sock) : sock_(sock) {
        std::cout << "Socket " << sock << " acquired." << std::endl;
    }

    ~Socket() {
        if (sock_ != -1) {
            close(sock_); // 关闭套接字
            std::cout << "Socket " << sock_ << " released." << std::endl;
        }
    }

    // 禁止拷贝
    Socket(const Socket&) = delete;
    Socket& operator=(const Socket&) = delete;

    // 允许移动
    Socket(Socket&& other) noexcept : sock_(other.sock_) {
        other.sock_ = -1;
    }

    Socket& operator=(Socket&& other) noexcept {
        if (this != &other) {
            if (sock_ != -1) {
                close(sock_);
            }
            sock_ = other.sock_;
            other.sock_ = -1;
        }
        return *this;
    }

    int get() const { return sock_; }

private:
    int sock_;
};

int main() {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        std::cerr << "Failed to create socket." << std::endl;
        return 1;
    }

    Socket socket(sock); // 自动管理套接字

    // 使用套接字进行操作

    return 0;
}

5. 临时文件管理

在一些应用程序中,临时文件需要在程序结束时自动删除,使用RAII可以方便地管理这种资源。

示例:自定义临时文件管理类

#include <iostream>
#include <cstdio>
#include <stdexcept>

class TempFile {
public:
    TempFile(const std::string& filename) : filename_(filename) {
        file_ = std::fopen(filename.c_str(), "w+");
        if (!file_) {
            throw std::runtime_error("Failed to open temporary file");
        }
        std::cout << "Temporary file " << filename << " created." << std::endl;
    }

    ~TempFile() {
        if (file_) {
            std::fclose(file_);
            std::remove(filename_.c_str());
            std::cout << "Temporary file " << filename_ << " deleted." << std::endl;
        }
    }

    // 禁止拷贝
    TempFile(const TempFile&) = delete;
    TempFile& operator=(const TempFile&) = delete;

    // 允许移动
    TempFile(TempFile&& other) noexcept : file_(other.file_), filename_(std::move(other.filename_)) {
        other.file_ = nullptr;
    }

    TempFile& operator=(TempFile&& other) noexcept {
        if (this != &other) {
            if (file_) {
                std::fclose(file_);
                std::remove(filename_.c_str());
            }
            file_ = other.file_;
            filename_ = std::move(other.filename_);
            other.file_ = nullptr;
        }
        return *this;
    }

    FILE* get() const { return file_; }

private:
    FILE* file_;
    std::string filename_;
};

int main() {
    try {
        TempFile tempFile("temp.txt"); // 自动管理临时文件

        // 使用 tempFile.get() 进行文件操作
        std::fprintf(tempFile.get(), "Temporary data\n");

    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return 1;
    }

    return 0;
}

通过自定义RAII类,可以大大简化资源管理的代码,提高程序的健壮性和可维护性。

reference

Previous原子变量|CAS操作|内存顺序|C++11NextPimpl设计模式|编译防火墙

Last updated 11 months ago

Was this helpful?

❤️‍🔥
🍀
🍏
https://learn.microsoft.com/en-us/cpp/cpp/?view=msvc-170