学习笔记 —— 基于C加速的Python高效计算 (Cython pybind11)

news2024/11/19 23:21:44

目录

  • 引言
  • Cython
    • 示例介绍
    • 第一阶段优化
    • 第二阶段优化
      • Cython Annotation tool
      • 优化方法
    • 第三阶段优化
    • 比对下 JIT的Numba
    • 总结
  • pybind11
    • Links
    • Introduction
    • Implementation
    • Implementation Cmake
    • cytpes
  • Cython & pybind11 性能比较

TODO
Implementation Cmake --pybind11
Cython & pybind11 性能比较

引言

Python与C++,是目前比较流行的两种计算机。Python 易用但效率低,C++复杂但效率高。 所以有很多利用C来给python加速的工具。
这些工具一般分为两类: AOT(Ahead of Time) 和 JIT(Just in Time)
但是JIT有"cold start" 的问题,并且即便过了"cold start" ,其效率依旧不如经过人工精致优化的AOT,唯一的优点就是方便易用。 比如Numba,函数上面加个“@jit()”就完事儿了。

个人偏好更有水平的AOT方法,所以本文来介绍两个主流的AOT工具:Cython 和 pybind11

安装很简单:

conda install numpy
conda install cython
conda install pybind11

Cython

示例介绍

使用Cython一般需要三个文件:cython格式编写的库文件cythonfn.pyx, 用于编译cythonfn.pyx的setup.py, 以及调用cython库的python代码 main.py
考虑一种运算:

...
def calculate_z_serial_purepython(maxiter, zs, cs):
    """Calculate output list using Julia update rule"""
    output = [0] * len(zs)
    for i in range(len(zs)):
        n = 0
        z = zs[i]
        c = cs[i]
        while abs(z) < 2 and n < maxiter:
            z = z * z + c
            n += 1
        output[i] = n
    return output
    
def calc_pure_python(desired_width, max_iterations):
    """Create a list of complex coordinates (zs) and complex parameters (cs),
    build Julia set"""
    x_step = (x2 - x1) / desired_width
    y_step = (y1 - y2) / desired_width
    x = []
    y = []
    ycoord = y2
    while ycoord > y1:
        y.append(ycoord)
        ycoord += y_step
    xcoord = x1
    while xcoord < x2:
        x.append(xcoord)
        xcoord += x_step
    # build a list of coordinates and the initial condition for each cell.
    # Note that our initial condition is a constant and could easily be removed,
    # we use it to simulate a real-world scenario with several inputs to our
    # function
    zs = []
    cs = []
    for ycoord in y:
        for xcoord in x:
            zs.append(complex(xcoord, ycoord))
            cs.append(complex(c_real, c_imag))

    output = [0] * len(zs)
    start_time = time.time()
    output = calculate_z_serial_purepython(max_iterations, zs, cs)    ## attention!!!
    end_time = time.time()
    secs = end_time - start_time
    print(calculate_z_serial_purepython.__name__ + " took", secs, "seconds")
    ...
if __name__ == "__main__":
    secs = calc_pure_python(desired_width=1000, max_iterations=300)

经过分析不难发现(学习笔记 —— python代码耗时及内存占用测试方法 以及一些零碎的python小工具),进行循环计算的如下函数是耗时大户,也是我们最需要优化的对象。
在这里插入图片描述
由于*.pyx本身是基于python的,因此利用Cython我们可以看到python到C的演变过程,以及各阶段的优化力度。
现在优化前,我的耗时是这样的:
在这里插入图片描述

第一阶段优化

编写cythonfn.pyx文件, 把calculate_z_serial_purepython()函数原封不动copy过来:

def calculate_z_serial_purepython(maxiter, zs, cs):
    """Calculate output list using Julia update rule"""
    output = [0] * len(zs)
    for i in range(len(zs)):
        n = 0
        z = zs[i]
        c = cs[i]
        while abs(z) < 2 and n < maxiter:
            z = z * z + c
            n += 1
        output[i] = n
    return output

编写setup.py

from distutils.core import setup
from Cython.Build import cythonize

setup(ext_modules=cythonize("cythonfn.pyx",
                            compiler_directives={'language_level': "3"}
                            ))

运行setup.py 编译cythonfn.pyx
在这里插入图片描述
你会发现多了两个文件:
在这里插入图片描述
现在我们用这个cython编译后的calculate_z_serial_purepython()替代之前的:

import cythonfn

def calc_pure_python(desired_width, max_iterations):
	...
    start_time = time.time()
    output = cythonfn.calculate_z_serial_purepython(max_iterations, zs, cs)   # cython
    end_time = time.time()
    secs = end_time - start_time
    print(calculate_z_serial_purepython.__name__ + " took", secs, "seconds")

在这里插入图片描述

效率快提高一倍了,厉害吧?但如标题所说,这才刚刚开始!
如果你图懒,觉得这就够了,建议你不要考虑AOT方法了,去找JIT的吧,比如Numba。

第二阶段优化

Cython Annotation tool

在第二阶段开始前,我们先介绍一个Cython自己的可视化工具,可以把Cython编译后的底层C代码展示给我们:

运行命令:

cython -a cythonfn.pyx

会生成一个 cythonfn.html的文件,打开:
在这里插入图片描述
现在我们通过点击原代码对应行左边的加号便可展开看到被编译后的底层C代码(我只知道你看不懂哈哈)。
我们需要关注的是颜色的深浅,颜色越深表示对Python virtual machine的调用越多,也就越慢; 纯白色表示就可以认为是C代码。

好了,现在我们的目标就是:让这个界面越白越好!

优化方法

Cython中可以通过 cdef 命令直接定义C层次的变量,避免Python的type识别:

def calculate_z_serial_purepython(maxiter, zs, cs):
    """Calculate output list using Julia update rule"""
    cdef unsigned int i,n
    cdef double complex z,c
    output = [0] * len(zs)
    for i in range(len(zs)):
        n = 0
        z = zs[i]
        c = cs[i]
        while abs(z) < 2 and n < maxiter:
            z = z * z + c
            n += 1
        output[i] = n
    return output

现在看是不是白了许多?尤其是最耗时的第14行,纯白色!这意味着完全脱离的Python到达了C的层次!
在这里插入图片描述

现在重新用 setup.py 编译后的结果如下:
在这里插入图片描述
比起最开始提高了一个数量级不止,有没有种恐怖如斯的感觉?

第三阶段优化

我们都知道python中常用numpy来进行科学计算,会加速很多。但numpy是一种封装好的类,而C喜欢直接对raw_data,如double[] 操作,因此np.array直接传递给C有点臃肿。

好在python使用了一种缓存协议(buffer protoco),可以直接把缓存中的数据传给C。

import numpy as np
cimport numpy as np

def calculate_z_serial_purepython(int maxiter, double complex[:] zs, double complex[:] cs):
    """Calculate output list using Julia update rule"""
    cdef unsigned int i,n
    cdef double complex z,c
    cdef int[:] output = np.empty(len(zs), dtype=np.int32)
    # output = [0] * len(zs)
    for i in range(len(zs)):
        n = 0
        z = zs[i]
        c = cs[i]
        # while abs(z) < 2 and n < maxiter:
        while  n < maxiter and z.real*z.real+z.imag*z.imag < 4:
            z = z * z + c
            n += 1
        output[i] = n
    return output

因为用了numpy,所以setup.py也得做相应改动:

from distutils.core import setup
from Cython.Build import cythonize
import numpy

setup(ext_modules=cythonize("cythonfn.pyx",
                            compiler_directives={'language_level': "3"}
                            ), include_dirs=[numpy.get_include()])           

现在相比之前是不是又白了很多?
在这里插入图片描述
在这里插入图片描述
又在第二阶段的基础上快了一倍。
没有很大提高是因为最耗时的第18行已经优化到极致了(不能更白了),针对这个函数只是优化了数据存取而已。

好了,经过三个阶段的优化,我们把原本的5.49s耗时缩减到了 0.17s,足足32倍!

比对下 JIT的Numba

函数上方加个“@jit()”

from numba import jit
@jit()
def calculate_z_serial_purepython_numba(maxiter, zs, cs, output):
    """Calculate output list using Julia update rule"""
    for i in range(len(zs)):
        n = 0
        z = zs[i]
        c = cs[i]
        while abs(z) < 2 and n < maxiter:
            z = z * z + c
            n += 1
        output[i] = n

看看效果:
在这里插入图片描述
只能说和第一阶段的cython等同。
差的那几百毫秒怕是“cold start”造成的。

总结

方法Time Usage
无优化5.4992077350616455 seconds
cython第一阶段3.16896915435791 seconds
cython第二阶段0.37267589569091797 seconds
cython第三阶段0.17330312728881836 seconds
Numba3.286240577697754 seconds

pybind11

Links

Pybind11 github
Pybind11 doc

Introduction

Cython使用了用C编写的独立共享库,并将它们转换为Python模块。实际还是在Python上写库。
PyBind11相反,它提供C++ API,写的是C++代码库,然后转成Python模块。
Pybind11只支持C++,使用C++ 11特性。
此外,Pybind11的另一个优点是可以轻松处理来自Python的底层数据和类型(如Numpy array)
小知识:TensorFlow也用了Pybind11。

Pybind11的编译可以用g++或者Cmake,二者在VS上都要有很多配置,个人觉得很麻烦,所以建议摆脱win,到linux上去用

Implementation

考虑一个AXPY形式 的Python 矩阵运算:
y ← α x + y y\leftarrow \alpha x +y yαx+y
AXPY 有两种变体:SAXPY 和 DAXPY。 分别对应 Single Precision (32-bit float) 和 Double Precision (64-bit double)
这意味着我们需要区分double和float类型才能正常使用pybind。

以SAXPY 为例,编写核函数:

void axpy(size_t n, float a, float *x, float *y)
{
  for (size_t i = 0; i < n; i++)
    y[i] = a * x[i] + y[i];
}

前面我们说Pybind11可以轻松处理Numpy array,这还是float* , 没看到Numpy啊,难道这就行了,可以自动转换?当然不行,自动就需要多余的识别,自然就慢了。

别着急,我们再写个 wrapper to handle the Numpy arrays:(使用模板类型 py::array_t<T> 来存取Numpy数据)

小知识:Python supports a very general and convenient way for exchanging data between libraries through a buffer protocol. In this sense, the py::array_t<T> can be considered a buffer object that is restricted to Numpy arrays. The information of the buffer, such as the dimension and size, are stored in an information object called py::buffer_info.

#include <pybind11/numpy.h>
using namespace py = pybind11;
void axpy_wrapper(float a, py::array_t<float> py_x, py::array_t<float> py_y)
{
  // Request for buffer information
  py::buffer_info x_buffer = py_x.request();
  py::buffer_info y_buffer = py_y.request();

  // check dim
  if (x_buffer.ndim != 1 || y_buffer.ndim != 1) {
    throw std::runtime_error("Error: dimension of vector should be 1");
  }

  // check shape match
  if (x_buffer.shape[0] != y_buffer.shape[0]) {
    throw std::runtime_error("Error: size of X and Y do not match");
  }

  // extract raw pointer
  float *x = (float*)x_buffer.ptr;
  float *y = (float*)y_buffer.ptr;
  
  return axpy(x_buffer.shape[0], a, x, y);
}

For more information on format checking and performance optimization, refer to here and here

完整example.cpp代码如下:

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>     // Include header that supports Numpy arrays

namespace py = pybind11;

void axpy(size_t n, float a, float *x, float *y)
{
  for (size_t i = 0; i < n; i++)
    y[i] = a * x[i] + y[i];
}

void axpy_wrapper(float a, py::array_t<float> py_x, py::array_t<float> py_y)
{
  // Request for buffer information
  py::buffer_info x_buffer = py_x.request();
  py::buffer_info y_buffer = py_y.request();

  // check dim
  if (x_buffer.ndim != 1 || y_buffer.ndim != 1) {
    throw std::runtime_error("Error: dimension of vector should be 1");
  }

  // check shape match
  if (x_buffer.shape[0] != y_buffer.shape[0]) {
    throw std::runtime_error("Error: size of X and Y do not match");
  }

  // extract raw pointer
  float *x = (float*)x_buffer.ptr;
  float *y = (float*)y_buffer.ptr;

  return axpy(x_buffer.shape[0], a, x, y);
}

PYBIND11_MODULE(math_kernels, m)
{
  m.def("axpy", &axpy_wrapper);
}

使用g++编译:

$ g++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

然后python中调用试试:

$ python
Python 3.8.0 (default, Nov  6 2019, 21:49:08) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> import example
>>> x = np.random.random(5)
>>> y = np.random.random(5)
>>> a = 0.5
>>> z = a * x + y
>>> example.axpy(a, x, y)
>>> z
array([0.72109651, 0.80642238, 1.3581158 , 0.68677673, 0.47000476])
>>> y
array([0.67480229, 0.67799412, 0.97670182, 0.20975148, 0.35173144])
>>> z - y
array([0.04629421, 0.12842827, 0.38141398, 0.47702525, 0.11827332])
>>> 

哈哈哈,pybind11的结果完全是错的!!!
还是数据类型的问题呀! Numpy arrays的默认数据类型是 np.float64,也就是 double, 咱们上面C库里写的可是float哦~
这下你知道即便是 floatdouble 甚至常常在C代码中都会被忽略数据类型冲突的两种类型,在pybind11里搞错后有多大影响了吧?

但是你有没有发现,数据类型不一样它不给你报错,然后给了个错的离谱的结果! 这点是很坑的!因为pybind11会做自动类型转换,还转不对!所以大家养好习惯,在PYBIND11_MODULE中做一些声明,不让他自动转:

/* name of module-        ---a variable with type py::module_ to create the binding */
/* (这个才是python里   |       |                                                                                                */
/*  import的库名)     v       v                                                                                               */
PYBIND11_MODULE(example, m) {                            // Create a module using the PYBIND11_MODULE macro
    m.doc() = "pybind11 math module";    // optional module docstringm.def("sum", &sum, "sum two numbers in double"); // calls module::def() to create generate binding code that exposes sum()
    m.def("axpy", &axpy_wrapper, py::arg("a"), py::arg("x").noconvert(true), py::arg("y").noconvert(true)); // 第一个参数是python调用时用的函数名
}

现在我们编译再试:

$ python
Python 3.8.0 (default, Nov  6 2019, 21:49:08) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> import example
>>> x = np.random.random(5).astype(np.float32)
>>> y = np.random.random(5)
>>> example.axpy(0.5, x, y)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: axpy(): incompatible function arguments. The following argument types are supported:
    1. (a: float, x: numpy.ndarray[float32], y: numpy.ndarray[float32]) -> None

Invoked with: 0.5, array([0.583261  , 0.4329178 , 0.1882612 , 0.94220173, 0.56544894],
      dtype=float32), array([0.65019744, 0.12314781, 0.04364232, 0.54704202, 0.18679054])
>>> 

这次你数据类型不对就别想跑通了嘿嘿。

但是我们C里定义的是模板T呀,我们加个double的声明不就完了:

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>    // Include header that supports Numpy arrays

namespace py = pybind11;

template <typename T>
void axpy(size_t n, T a, T *x, T *y)
{
  for (size_t i = 0; i < n; i++)
    y[i] = a * x[i] + y[i];
}

template <typename T>
void axpy_wrapper(T a, py::array_t<T> py_x, py::array_t<T> py_y)
{
  // Request for buffer information
  py::buffer_info x_buffer = py_x.request();
  py::buffer_info y_buffer = py_y.request();

  // check dim
  if (x_buffer.ndim != 1 || y_buffer.ndim != 1) {
    throw std::runtime_error("Error: dimension of vector should be 1");
  }

  // check shape match
  if (x_buffer.shape[0] != y_buffer.shape[0]) {
    throw std::runtime_error("Error: size of X and Y not match");
  }

  // extract raw pointer
  T *x = (T*)x_buffer.ptr;
  T *y = (T*)y_buffer.ptr;
  
  return axpy<T>(x_buffer.shape[0], a, x, y);
}

PYBIND11_MODULE(math_kernels, m)
{
  m.def("axpy", &axpy_wrapper<float>,  py::arg("a"), py::arg("x").noconvert(true), py::arg("y").noconvert(true));
  m.def("axpy", &axpy_wrapper<double>, py::arg("a"), py::arg("x").noconvert(true), py::arg("y").noconvert(true));
}

再试试:

$ python
Python 3.8.0 (default, Nov  6 2019, 21:49:08) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> import example
>>> x = np.random.random(5).astype(np.float32)
>>> y = np.random.random(5).astype(np.float32)
>>> a = 0.5
>>> z = a * x + y
>>> example.axpy(a, x, y)
>>> y - z
array([0., 0., 0., 0., 0.], dtype=float32)
>>> x = np.random.random(5).astype(np.float64)
>>> y = np.random.random(5).astype(np.float64)
>>> z = a * x + y
>>> example.axpy(a, x, y)
>>> y - z
array([0., 0., 0., 0., 0.])
>>>

OK,现在你只要保证输入数据类型同时是 float 或者同时是 double 就行啦。

现在你可以尝试一下用pybind11实现上面Cython中提到的**calculate_z_serial_purepython()**看看有多少提高啦!

Implementation Cmake

如果只是简单测试,g++当然足够了。但大多数情况下,我们需要Cmake来管理一整个项目,因此如果使用Cmake编译pybind11变得很重要!

TODO

cytpes

还有一种方法与pybind11很像, ctypes,都是用g++编译后调用。
不过个人感觉pybind11更方便一点,效率暂且没做比较
下面是ctypes的一个简单示例:

定义C源文件 sum_src.c.

#include <stdio.h>
#include "sum.h"

double sum(double a, double b)
{
  printf("hello world from sum()\n");
  return a + b;
}
```h
和头文件**sum.h**:
```cpp
#ifndef __SUM_H__
#define __SUM_H__
double sum(double, double);
#endif

gcc编译为 libsum.so:

gcc -fPIC -shared -o libsum.so sum_src.c

然后python调用:

Python 3.8.0 (default, Nov  6 2019, 21:49:08) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> _libsum = ctypes.CDLL('libsum.so')	# 加载共享库,定义函数签名,以便ctypes可以转换类型
>>> _libsum.sum.argtypes = [ctypes.c_double, ctypes.c_double]	# 指定参数类型
>>> _libsum.sum.restype = ctypes.c_double 	# 指定返回类型
>>> _libsum.sum(ctypes.c_double(3.1), ctypes.c_double(3.3)) # 函数调用
hello world from sum()
6.4
>>> 

python调的时候还需要再指定遍类型,是不是有点麻烦?另外,所有参数都必须转换为各自的基本数据类型, 也就是你用numpy的话还得转一下,而不是pybind那种可以直接调用。
ctypes的一个特性是可以定义回调函数以允许C程序调用Python函数。有关更多信息,请参阅 文档。

Cython & pybind11 性能比较

容易理解,Cython还是在pyhon上编写的,依旧需要Python的一些相对于C++比较多余的解码机制;而pybind11直接在C++写库,并且一定要指定数据类型,没有多余的解码,自然会快一些。

Pybind11 github
Pybind11 doc

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/388302.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux SPI 驱动实验

目录 一、Linux 下 SPI 驱动框架简介 1、SPI 主机驱动 2、SPI 设备驱动 SPI 设备数据收发处理流程 3、SPI 设备和驱动匹配过程 二、添加SPI 设备信息 1、添加 ICM20608 所使用的 IO 2、 在 ecspi3 节点追加 icm20608 子节点 三、编写 ICM20608 驱动 1、修改makefile​…

深度学习目标检测ui界面-交通标志检测识别

深度学习目标检测ui界面-交通标志检测识别 为了将算法封装起来&#xff0c;博主尝试了实验pyqt5的上位机界面进行封装&#xff0c;其中遇到了一些坑举给大家避开。这里加载的训练模型参考之前写的博客&#xff1a; 自动驾驶目标检测项目实战(一)—基于深度学习框架yolov的交通…

C++ 二级指针的理解

指针一直很抽象&#xff0c;不是很容易理解&#xff0c;尤其是二级指针。有天路上&#xff0c;脑子里突然想起二级指针&#xff0c;并开始思考什么是二级指针&#xff0c;经过分析和调试验证&#xff0c;对二级指针有了进一步的认识。故写下此篇。 一级指针 假设变量 a、p 的…

消息队列 rabbitmq 学习

RabbitMQ是一个在AMQP基础上完整的&#xff0c;可复用的企业消息系统。他遵循Mozilla Public License开源协议。 MQ全称为Message Queue, 消息队列&#xff08;MQ&#xff09;是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息&#xff08;针对应用程序的数…

[YOLO] yolov4 博客笔记

在有了yolov3的基础上&#xff0c;yolov4的网络结构还是很容易看懂的。 这篇博客写的整个yolov4的网络概况 【经典论文解读】YOLOv4 目标检测https://blog.csdn.net/qq_41204464/article/details/119673960?ops_request_misc%257B%2522request%255Fid%2522%253A%25221677916…

SpringBoot一行代码实现文件上传20个平台!少写代码到极致!

又是做好人好事的一天&#xff0c;有个小可爱私下问我有没有好用的springboot文件上传工具&#xff0c;这不巧了嘛&#xff0c;正好我私藏了一个好东西&#xff0c;顺便给小伙伴们也分享一下&#xff0c;demo地址放在文末了。文件上传在平常不过的一个功能&#xff0c;做后端开…

SpringBoot (三) 整合数据库访问 jdbcTemplate、MyBatis

哈喽&#xff0c;大家好&#xff0c;我是有勇气的牛排&#xff08;全网同名&#xff09;&#x1f42e;&#x1f42e;&#x1f42e; 有问题的小伙伴欢迎在文末评论&#xff0c;点赞、收藏是对我最大的支持&#xff01;&#xff01;&#xff01;。 Spring Data了解下&#xff1…

实习日记_C#——Day2

运算符重载 通过关键字operator后跟运算符的符号来定义。 public static Box operator (Box b, Box c) {Box box new Box();box.height b.height c.height;return box; }Box box1 new Box(1); Box box2 new Box(2); Box box3 new Box(); bo3 box1 box2;接口&#xff…

基于灵动 MM32 微控制器的便携式血氧仪方案

基于灵动 MM32 微控制器的便携式血氧仪&#xff1a; - Cortex-M0() 最高主频 72MHz 可实现血氧饱和度信号采集、算法操作和 LED 显示操作 - 高性能的 1Msps 12b ADC 能对光电采样结果进行大数据量的暂存和处理&#xff0c;提高采样的效率并有助于对结果做高精度的计算 - 100…

排序中常见的一些指标

1、错误率与精度 错误率与精度是分类任务中最常用的两种性能度量&#xff0c;错误率是指分类错误的样本占样本总数的比例&#xff0c;精度则是分类正确的样本数占样本总数的比例。 错误率&#xff1a; 精度&#xff1a; 2、准确率/召回率/FScore True Positive(真正例, TP)&…

SpringMVC中的常用注解

Java知识点总结&#xff1a;想看的可以从这里进入 目录3.2、常用的注解3.2、常用的注解 Controller&#xff1a;代表此类是一个控制器&#xff0c;需要配置包的扫描。Spring MVC 是通过组件扫描机制查找应用中的控制器类的 在Spring6.0之后要求控制层必须添加该注解才会被识别成…

c++11 标准模板(STL)(std::unordered_map)(二)

定义于头文件 <unordered_map> template< class Key, class T, class Hash std::hash<Key>, class KeyEqual std::equal_to<Key>, class Allocator std::allocator< std::pair<const Key, T> > > class unordered…

计算机网络-- 网络层(day06)

文章目录网络层思维导图IPv4地址的应用规划定长的子网掩码变长的子网掩码VLSMIP数据报的发送和转发过程主机发送IP数据报路由器转发IP数据报静态路由选择动态路由选择路由选择协议概述常见的路由选择协议路由器的基本结构路由信息协议RIP的基本工作原理开放最短路径优先OSPF的基…

nandflash VS norflash

介绍下nandflash NAND Flash是一种非易失性存储器&#xff08;Non-Volatile Memory&#xff0c;简称NVM&#xff09;&#xff0c;以闪存为基础&#xff0c;是目前应用最广泛的存储器之一。 它利用了固态电路技术&#xff0c;可以将数百万个晶体管集成在一张芯片上&#xff0c…

从输入URL到Web页面呈现的全过程

当用户在浏览器的地址栏中输入 URL 并点击回车后&#xff0c;页面是如何呈现的。 简单来说&#xff0c;当用户在浏览器的地址栏中输入 URL 并点击回车后&#xff0c;浏览器从服务端获取资源&#xff0c;然后将内容显示在页面上。这个过程经过了&#xff1a;浏览器缓存 -> D…

利用Nginx给RStudio-Server配置https

前篇文档&#xff0c;我这边写了安装RStudio-Server的方法。默认是http的访问方式&#xff0c;现在我们需要将其改成https的访问方式。 1、给服务器安装Nginx&#xff1a;参照之前的安装Nginx的方法。 2、创建/usr/local/nginx/ssl目录&#xff1a; mkdir /usr/local/nginx/ss…

P6入门:了解P6 Professional 工具栏及地图分享

目录 引言 相关分享 引言 凭借更大的灵活性和增强的自定义功能&#xff0c;最新版本的 Oracle Primavera P6 Professional 的界面比早期版本有了巨大改进。对于有经验的伙伴来说&#xff0c;它仍然是熟悉的领域&#xff0c;几乎所有预期的功能都显示在前面。该界面可以更好地…

【汇编】三、寄存器(一只 Assember 的成长史)

嗨~你好呀&#xff01; 我是一名初二学生&#xff0c;热爱计算机&#xff0c;码龄两年。最近开始学习汇编&#xff0c;希望通过 Blog 的形式记录下自己的学习过程&#xff0c;也和更多人分享。 上篇系列文章链接&#xff1a;【汇编】二、预备知识&#xff08;一只 Assember 的…

Spring Boot结合IDEA自带Maven插件快速切换profile | Spring Cloud 10

一、前言 IDEA是目前 Java 开发者中使用最多的开发工具&#xff0c;它有着简约的设计风格&#xff0c;强大的集成工具&#xff0c;便利的快捷键。 在项目项目整个开发运维周期中&#xff0c;我们的的项目往往需要根据不同的环境&#xff0c;使用不同的文件配置。 比如以下部…

机器学习:Transformer

Transformer sequence-to-sequence(seq2seq) 很大语音没有文本&#xff0c;7000种中超半数没有文字。 遇到的问题&#xff1a; 遇到问题时候可以先不管它&#xff0c;先出一个baseline看看效果&#xff0c;后续再进行提升。 tts&#xff1a; 文本转语音&#xff0c;语音合成…