setup.py实现C++扩展和python库编译

        很多大的程序使用前都会需要调用setup.py进行build编译并安装自己的库,debug发现程序有时候走到了编译出的lib里面去了,在源程序改动没用,有的运算放在cuda上也是编译完成接口调用的,有必要了解一下这个setup.py是怎么工作的。

目录

安装方式

setuptools 

setup函数

作用


目的

编写setup.py是为了实现python的C/C++扩展。比如自定义层实现自己的全新网络算法,理论上继承nn.Module编写forward函数即可自动实现反向传播,但是pytorch的函数针对特定的操作进行了优化,组合起来效率可能很低,无法充分利用GPU通道或者超负载,而且python解释器也无法优化。所以一般是用C++编写相关算法(如RoIAlign , NMS)的程序,充分利用GPU资源,然后作为扩展程序在pytorch进行导入即可,这部分就是setyp.py完成的。

cuda程序

其中主要是GPU的扩展,cuda的程序后缀.cu,头文件也是.h,是基于C++的改进,支持大多C++语法并加入一些特别的语法。

这个链接实现了一个简单的cuda的C++扩展python程序https://oldpan.me/archives/pytorch-cuda-c-plus-plus

下面是Mask R-CNN的setup.py,以看懂这个为中心展开说明和学习:

# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
#!/usr/bin/env python

import glob
import os

import torch
from setuptools import find_packages
from setuptools import setup
from torch.utils.cpp_extension import CUDA_HOME
from torch.utils.cpp_extension import CppExtension
from torch.utils.cpp_extension import CUDAExtension

requirements = ["torch", "torchvision"]

def get_extensions():
    this_dir = os.path.dirname(os.path.abspath(__file__))
    extensions_dir = os.path.join(this_dir, "maskrcnn_benchmark", "csrc")

    main_file = glob.glob(os.path.join(extensions_dir, "*.cpp"))
    source_cpu = glob.glob(os.path.join(extensions_dir, "cpu", "*.cpp"))
    source_cuda = glob.glob(os.path.join(extensions_dir, "cuda", "*.cu"))

    sources = main_file + source_cpu
    extension = CppExtension

    extra_compile_args = {"cxx": []}
    define_macros = []

    if torch.cuda.is_available() and CUDA_HOME is not None:
        extension = CUDAExtension
        sources += source_cuda
        define_macros += [("WITH_CUDA", None)]
        extra_compile_args["nvcc"] = [
            "-DCUDA_HAS_FP16=1",
            "-D__CUDA_NO_HALF_OPERATORS__",
            "-D__CUDA_NO_HALF_CONVERSIONS__",
            "-D__CUDA_NO_HALF2_OPERATORS__",
        ]

    sources = [os.path.join(extensions_dir, s) for s in sources]

    include_dirs = [extensions_dir]

    ext_modules = [
        extension(
            "maskrcnn_benchmark._C",
            sources,
            include_dirs=include_dirs,
            define_macros=define_macros,
            extra_compile_args=extra_compile_args,
        )
    ]

    return ext_modules

setup(
    name="maskrcnn_benchmark",
    version="0.1",
    author="fmassa",
    url="https://github.com/facebookresearch/maskrcnn-benchmark",
    description="object detection in pytorch",
    packages=find_packages(exclude=("configs", "tests",)),
    # install_requires=requirements,
    ext_modules=get_extensions(),
    cmdclass={"build_ext": torch.utils.cpp_extension.BuildExtension},
)

安装方式

执行的命令是python setup.py build develop,涉及包的安装的主要方式如下:

  • 开发方式安装
    python setup.py develop

    如果应用在开发过程中会频繁变更,每次安装还需要先将原来的版本卸掉,很麻烦。使用”develop”开发方式安装的话,应用代码不会真的被拷贝到本地Python环境的”site-packages”目录下,而是在”site-packages”目录里创建一个指向当前应用位置的链接。这样如果当前位置的源码被改动,就会马上反映到”site-packages”里。(mmdetection就是直接创建在site-packages的)

  • 安装应用

    python setup.py install

    很多方式都是这种,会将当前的Python应用安装到当前Python环境的”site-packages”目录下,这样其他程序就可以像导入标准库一样导入该应用的代码了。

setuptools 

这个是发布库的主要工具,非标准库需要自行pip安装,据说是高手都用这个(网上很多说的distutils是标准库,功能不够多)。从中import的setup函数是setup.py的主要部分。

官方文档:https://setuptools.readthedocs.io/en/latest/setuptools.html#metadata

setup函数

传入的参数类型为:

name包名称
version包版本
author程序的作者
author_email程序的作者的邮箱地址
url程序的官网地址
description程序的简单描述
classifiers程序的所属分类列表
packages需要处理的包目录(通常为包含 __init__.py 的文件夹)
cmdclass添加自定义命令
exclude_package_data当 include_package_data 为 True 时该选项用于排除部分文件
ext_modules指定扩展模块
zip_safe不压缩包,而是以目录的形式安装

更多参数见文档:https://setuptools.readthedocs.io/en/latest/setuptools.html#metadata

以及介绍:http://blog.konghy.cn/2018/04/29/setup-dot-py/   http://www.bjhee.com/setuptools.html

针对mask rcnn的几个参数不难看出一些简单的规则,不影响包的发布:

setup(
    name="maskrcnn_benchmark",    包名称
    version="0.1",                版本号
    author="fmassa",              发布者(facebook的一个工程师github的id)
    url="https://github.com/facebookresearch/maskrcnn-benchmark",    repo的url
    description="object detection in pytorch",                        描述
    packages=find_packages(exclude=("configs", "tests",)),
    # install_requires=requirements,            安装依赖项->列表的torch两项
    ext_modules=get_extensions(),
    cmdclass={"build_ext": torch.utils.cpp_extension.BuildExtension},
)

其中比较麻烦的是下面几个:

  • packages: python的package是包含__init__.py的文件夹;find_packages(exclude=("configs", "tests",))是递归地包含当前目录下除了configs和tests外所有文件夹的包(主要都在maskrcnn_benchmark下)。断点查看包的目录:
    ['maskrcnn_benchmark', 'maskrcnn_benchmark.structures', 'maskrcnn_benchmark.modeling', 'maskrcnn_benchmark.layers', 'maskrcnn_benchmark.data', 'maskrcnn_benchmark.config', 'maskrcnn_benchmark.solver', 'maskrcnn_benchmark.engine', 'maskrcnn_benchmark.utils', 'maskrcnn_benchmark.modeling.rpn', 'maskrcnn_benchmark.modeling.backbone', 'maskrcnn_benchmark.modeling.roi_heads', 'maskrcnn_benchmark.modeling.detector', 'maskrcnn_benchmark.modeling.rpn.retinanet', 'maskrcnn_benchmark.modeling.roi_heads.box_head', 'maskrcnn_benchmark.modeling.roi_heads.keypoint_head', 'maskrcnn_benchmark.modeling.roi_heads.mask_head', 'maskrcnn_benchmark.data.transforms', 'maskrcnn_benchmark.data.datasets', 'maskrcnn_benchmark.data.samplers', 'maskrcnn_benchmark.data.datasets.evaluation', 'maskrcnn_benchmark.data.datasets.evaluation.voc', 'maskrcnn_benchmark.data.datasets.evaluation.coco']

    很长....很多看不懂的工作放在这里了

  • ext_modules: 该参数用于构建 C / C++ 扩展扩展包。用于描述扩展模块的列表(列表每个元素对应一个模块),扩展模块可以设置扩展包名,头文件、源文件、链接库及其路径、宏定义和编辑参数等。

       可以看看这个函数的内部发生了什么:

def get_extensions():
    this_dir = os.path.dirname(os.path.abspath(__file__))
    extensions_dir = os.path.join(this_dir, "maskrcnn_benchmark", "csrc")
'''
this_dir: /py/MaskRcnn/maskrcnn-benchmark
extensions_dir: /py/MaskRcnn/maskrcnn-benchmark/maskrcnn_benchmark/csrc
'''
    main_file = glob.glob(os.path.join(extensions_dir, "*.cpp"))
    source_cpu = glob.glob(os.path.join(extensions_dir, "cpu", "*.cpp"))
    source_cuda = glob.glob(os.path.join(extensions_dir, "cuda", "*.cu"))
'''
可以看出source_cpu和source_cuda分别是一些写在cpu和gpu的实现,如nms,roialign等
main_file: ['/py/MaskRcnn/maskrcnn-benchmark/maskrcnn_benchmark/csrc/vision.cpp']
source_cpu: ['/py/MaskRcnn/maskrcnn-benchmark/maskrcnn_benchmark/csrc/cpu/nms_cpu.cpp', '/py/MaskRcnn/maskrcnn-benchmark/maskrcnn_benchmark/csrc/cpu/ROIAlign_cpu.cpp']
source_cuda: ['/py/MaskRcnn/maskrcnn-benchmark/maskrcnn_benchmark/csrc/cuda/ROIAlign_cuda.cu', '/py/MaskRcnn/maskrcnn-benchmark/maskrcnn_benchmark/csrc/cuda/ROIPool_cuda.cu', '/py/MaskRcnn/maskrcnn-benchmark/maskrcnn_benchmark/csrc/cuda/nms.cu', '/py/MaskRcnn/maskrcnn-benchmark/maskrcnn_benchmark/csrc/cuda/SigmoidFocalLoss_cuda.cu']
'''
    sources = main_file + source_cpu
    extension = CppExtension

    extra_compile_args = {"cxx": []}
    define_macros = []
    if torch.cuda.is_available() and CUDA_HOME is not None:
        extension = CUDAExtension
        sources += source_cuda    
        define_macros += [("WITH_CUDA", None)]
        extra_compile_args["nvcc"] = [
            "-DCUDA_HAS_FP16=1",
            "-D__CUDA_NO_HALF_OPERATORS__",
            "-D__CUDA_NO_HALF_CONVERSIONS__",
            "-D__CUDA_NO_HALF2_OPERATORS__",
        ]
    sources = [os.path.join(extensions_dir, s) for s in sources]
'''这里的source把mainfile和cpu,cuda的源码都放进去了'''
    include_dirs = [extensions_dir]

    ext_modules = [
        extension(
            "maskrcnn_benchmark._C",
            sources,
            include_dirs=include_dirs,
            define_macros=define_macros,
            extra_compile_args=extra_compile_args,
        )
    ]
    return ext_modules

该对象最后是:

 设计cuda编程和调用的方法,暂时略过。

  • cmdclass:自定义的命令,字典形式创建,此处没用到。(如果项自己重写run方法可以继承相关基类进行自定义)

作用

感觉build之后的好处和必要性是:可以全局直接调用相关的库函数,不用受目录结构的限制;链接cuda的cu文件,将放到cuda的操作通过接口链接;develope模式安装时不用像mmdetection一样去site-packages里面找lib的文件进行改动,可以源码直接操作。

 

 

 

 

 

  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: python setup.py build_ext 是一个命令,用于在Python中构建扩展模块。它会编译C或C++代码,并将其转换为Python可用的二进制格式。这个命令通常用于安装Python扩展模块或库。python setup.py build_ext是用于构建Python扩展模块的命令。它会编译C/C++代码并将其打包成Python可导入的模块。这个命令通常用于安装第三方Python库时,需要先编译扩展模块才能使用。 ### 回答2: python setup.py build_ext命令指的是Python中的扩展构建命令,它用于为特定系统和Python版本生成Python C扩展模块。Python C扩展模块是一种Python中调用C/C++库和功能的方法,这些库或功能在Python中没有原生的支持。 使用python setup.py build_ext命令需要满足以下条件: 首先,需要在Python中安装setuptools和wheel这两个库,这两个库都可以通过pip安装: ``` pip install setuptools pip install wheel ``` 然后,需要编写setup.py文件,在其中指定需要构建的扩展模块信息。这个文件通常会包括一些元信息,例如扩展模块名称、版本号、作者、简介等,以及扩展模块所需的源文件、编译器选项、链接器选项等。 最后,运行python setup.py build_ext命令来执行构建操作。Python会根据setup.py文件中的信息,解析出需要构建的扩展模块的相关源码、编译选项等,并执行相应的构建操作。构建成功后,会在dist目录下生成扩展模块的二进制文件。 需要注意的是,在执行python setup.py build_ext命令之前,可能需要在系统上安装一些依赖库和工具链,例如C/C++编译器、Python开发包等。如果在构建过程中出现任何错误或警告信息,需要检查和解决问题后重新执行构建命令。 总的来说,python setup.py build_ext命令是Python中非常重要的构建命令之一,它可以为Python程序提供强大的扩展能力,让Python开发人员能够更好地利用C/C++库和功能,从而实现更加复杂和高效的程序。 ### 回答3: Python是一门高级的编程语言,能够快速且高效地编写各种类型的软件。在Python中,通常使用setup.py文件来构建和打包Python软件,这个文件通常包括了构建和安装Python模块所需的信息和指令。其中,build_ext是一个命令,用于构建Python模块的C扩展,并且将C代码编译Python扩展模块。 build_ext命令有很多选项,例如可以指定编译器,调试标志或其他构建选项,以使扩展模块可以与Python解释器和其他第三方库兼容。通过使用build_ext,我们可以将纯Python代码转换成C代码,并将其编译为本地机器代码,以便在Python中使用。 通常,我们使用以下命令来构建Python扩展模块: python setup.py build_ext –inplace 这个命令告诉Python编译器将C代码编译为本地机器代码,然后将其链接到Python解释器中。在这个命令中,-inplace选项告诉Python将中间编译产生的文件放在当前目录中,而不是在build目录中。 总之,使用build_ext函数可以用C语言编写高性能的扩展模块,为Python程序提供更高的执行效率和更丰富的功能。同时,Pythonsetup.py文件的强大功能也为软件的开发、构建和打包提供了强有力的支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值