【机器学习】树模型的三种序列化方式的区别(模型存储大小、序列化所用内存、序列化速度)

news2024/11/23 16:45:44

文章目录

  • 一、导读
  • 二、模型运行例子
  • 三、运行内存计算
  • 四、保存和加载
    • 4.1 jsonpickle
    • 4.2 pickle
    • 4.3 模型自带
  • 五、实验
    • 5.1 模型存储大小对比实验
    • 5.2 运行的memory对比实验
  • 六、序列化时间对比
  • 七、源代码
  • 八、总结

一、导读

本文总结常用树模型: rf,xgboost,catboost和lightgbm等模型的保存和加载(序列化和反序列化)的多种方式,并对多种方式从运行内存的使用和存储大小做对比。

安装环境:

pip install xgboost
pip install lightgbm
pip install catboost
pip install scikit-learn

可以指定版本也可以不指定,直接下载可获取最新的pkg。

二、模型运行例子

针对iris数据集的多分类任务:

import xgboost as xgb
from catboost import CatBoostClassifier
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

import lightgbm as lgb
from sklearn.ensemble import RandomForestClassifier


iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

# xgb
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
xgb_params = {'objective': 'multi:softmax', 'eval_metric': 'mlogloss', 'num_class': 3, 'verbosity': 0}
xgb_model = xgb.train(xgb_params, xgb_train)
y_pred = xgb_model.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)

# lgb
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
params = {
    'boosting_type': 'gbdt',
    'objective': 'multiclass',
    'num_class': 3,
    'metric': 'multi_logloss',
    'num_leaves': 31,
    'learning_rate': 0.05,
    'feature_fraction': 0.9
}
gbm = lgb.train(params, lgb_train, num_boost_round=100, valid_sets=[lgb_eval], early_stopping_rounds=5)
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration)
y_pred = [list(x).index(max(x)) for x in y_pred]
lgb_acc = accuracy_score(y_test, y_pred)

# rf
rf = RandomForestClassifier()
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
rf_acc = accuracy_score(y_test, y_pred)

# catboost
cat_boost_model = CatBoostClassifier(depth=9, learning_rate=0.01,
                                     loss_function='MultiClass', custom_metric=['AUC'],
                                     eval_metric='MultiClass', random_seed=1996)

cat_boost_model.fit(X_train, y_train, eval_set=(X_test, y_test), use_best_model=True, early_stopping_rounds=1000)
y_pred = cat_boost_model.predict(X_test)
cat_acc = accuracy_score(y_test, y_pred)

print(xgb_acc, lgb_acc, rf_acc, cat_acc)

三、运行内存计算

def cal_current_memory():
    # 获取当前进程内存占用。
    pid = os.getpid()
    p = psutil.Process(pid)
    info = p.memory_full_info()
    memory_used = info.uss / 1024. / 1024. / 1024.
    return {
        'memoryUsed': memory_used
    }

获取当前进程的pid,通过pid来定向查询memory的使用。

四、保存和加载

主要有三种方法:

  1. jsonpickle
  2. pickle
  3. 模型api

4.1 jsonpickle

jsonpickle 是一个 Python 序列化和反序列化库,它可以将 Python 对象转换为 JSON 格式的字符串,或将 JSON 格式的字符串转换为 Python 对象。

调用jsonpickle.encode即可序列化,decode进行反序列化

以xgb为例

保存:

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

# xgb
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
xgb_params = {'objective': 'multi:softmax', 'eval_metric': 'mlogloss', 'num_class': 3, 'verbosity': 0}
xgb_model = xgb.train(xgb_params, xgb_train)
y_pred = xgb_model.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)

xgb_str = jsonpickle.encode(xgb_model)
with open(f'{save_dir}/xgb_model_jsonpickle.json', 'w') as f:
    f.write(xgb_str)

加载:

save_dir = './models'

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

xgb_test = xgb.DMatrix(X_test, y_test)

with open(f'{save_dir}/xgb_model_jsonpickle.json', 'r') as f:
    xgb_model_jsonpickle = f.read()
xgb_model_jsonpickle = jsonpickle.decode(xgb_model_jsonpickle)
y_pred = xgb_model_jsonpickle.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)
print(xgb_acc)

这样就完成了模型的保存和加载。

优势:

  1. 模型加载过程不需要重新实例化,直接jsonpickle.decode模型文件即可直接获得模型
  2. 获得的模型文件是json格式,便于各种编程语言和平台之间的数据交换,方便实现不同系统之间的数据传输和共享

劣势:

  1. 在处理大型或者复杂的模型时,序列化过程可能会出现性能问题(占用更多的memory)
  2. 模型文件存储空间比较大

4.2 pickle

pickle 是 Python 的一种序列化和反序列化模块,可以将 Python 对象转换为字节流,也可以将字节流转换为 Python 对象,进而实现 Python 对象的持久化存储和恢复。(模型也是个对象)

调用pickle.dump/dumps即可序列化,pickle.load/loads进行反序列化(其中dump直接将序列化文件保存,二dumps则是返回序列化后的bytes文件,load和loads亦然)

这里可以查看和其他python方法的对比:https://docs.python.org/zh-cn/3/library/pickle.html

以xgb为例

保存:

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

# xgb
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
xgb_params = {'objective': 'multi:softmax', 'eval_metric': 'mlogloss', 'num_class': 3, 'verbosity': 0}
xgb_model = xgb.train(xgb_params, xgb_train)
y_pred = xgb_model.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)

with open(f'{save_dir}/xgb_model_pickle.pkl', 'wb') as f:
    pickle.dump(xgb_model, f)

加载:

save_dir = './models'

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

xgb_test = xgb.DMatrix(X_test, y_test)

with open(f'{save_dir}/xgb_model_pickle.pkl', 'rb') as f:
    xgb_model_pickle = pickle.load(f)
y_pred = xgb_model_pickle.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)
print(xgb_acc)

模型加载过程同样不需要重新实例化,这点和jsonpickle一样;序列化文件相比于jsonpickle小非常的多,且读取和保存都会更快。

在处理大型或者复杂的对象时,可能会出现性能问题(占用更多的memory);不是json格式,很难跨平台和语言使用。

4.3 模型自带

以xgb为例

保存:

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

# xgb
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
xgb_params = {'objective': 'multi:softmax', 'eval_metric': 'mlogloss', 'num_class': 3, 'verbosity': 0}
xgb_model = xgb.train(xgb_params, xgb_train)
y_pred = xgb_model.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)

model_path = f'{save_dir}/xgb_model_self.bin' #也可以是json格式,但最终文件大小有区别 
xgb_model.save_model(model_path)

加载:

save_dir = './models'

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

xgb_test = xgb.DMatrix(X_test, y_test)

xgb_model_self = xgb.Booster()
xgb_model_self.load_model(f'{save_dir}/xgb_model_self.bin')
y_pred = xgb_model_self.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)
print(xgb_acc)

只保存了模型的参数文件(包含树结构和需要模型参数比如 the objective function等), 模型文件较小;序列化过程中的运行内存所占不多;也可以保存json的形式(在XGBoost 1.0.0之后推荐以json的方式保存)。

需要在加载模型之前创建模型的实例。

五、实验

以下主要还是针对较小的模型来做的实验。

5.1 模型存储大小对比实验

在这里插入图片描述
_jsonpickle就是用jsonpickle方法序列化的模型文件

_pickle是用pickle方法序列化的模型文件

_self就是利用自身的save model的方法保存的模型文件

可以看出来是 jsonpickle> pickle > self 的关系。

5.2 运行的memory对比实验

通过对序列化前后的memory做监控,例如xgb(只考虑序列化,去掉文件写入所需要的memory):

print("before:", cal_current_memory())
model_path = f'{save_dir}/xgb_model_self.bin'
xgb_model.save_model(model_path)
print("after:", cal_current_memory())

运行结果:

before: {'memoryUsed': 0.1490936279296875}
after: {'memoryUsed': 0.14911270141601562}
print("before:", cal_current_memory())
pickle.dumps(xgb_model)
print("after:", cal_current_memory())

运行结果:

before: {'memoryUsed': 0.1498260498046875}
after: {'memoryUsed': 0.14990234375}
print("before:", cal_current_memory())
xgb_str = jsonpickle.encode(xgb_model)
print("after:", cal_current_memory())

运行结果:

before: {'memoryUsed': 0.14917755126953125}
after: {'memoryUsed': 0.15140914916992188}

可以看出来对于xgb模型,picklejson所需要的memory是其他两种方法的几十倍,而其余两种方法很相似

lgb的结果:

对应上述顺序:

self:
before: {'memoryUsed': 0.14953994750976562}
after {'memoryUsed': 0.14959716796875}
pickle:
before: {'memoryUsed': 0.14938735961914062}
after {'memoryUsed': 0.14946746826171875}
jsonpickle:
before: {'memoryUsed': 0.14945602416992188}
after {'memoryUsed': 0.14974594116210938}

这里依然是jsonpickle大一些,但倍数小一些。

catboost的结果:

self:
before: {'memoryUsed': 0.24615478515625}
after {'memoryUsed': 0.25492095947265625}
pickle:
before: {'memoryUsed': 0.2300567626953125}
after {'memoryUsed': 0.25820159912109375}
jsonpickle:
before: {'memoryUsed': 0.2452239990234375}
after {'memoryUsed': 0.272674560546875}

六、序列化时间对比

因为catboost总体模型大小大一些,所以通过catboost才能更好的反应序列化的速度:

self:
0.02413797378540039 s
pickle:
0.04681825637817383 s
jsonpickle:
0.3211638927459717  s

jsonpickle的花费的时间会多一些。

七、源代码

import base64
import json
import os
import pickle
import time
import jsonpickle
import psutil
import xgboost as xgb
from catboost import CatBoostClassifier
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

import lightgbm as lgb
from sklearn.ensemble import RandomForestClassifier

save_dir = "./models"


def cal_current_memory():
    # 获取当前进程内存占用。
    pid = os.getpid()
    p = psutil.Process(pid)
    info = p.memory_full_info()
    memory_used = info.uss / 1024. / 1024. / 1024.
    return {
        'memoryUsed': memory_used
    }


iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

# xgb
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
xgb_params = {'objective': 'multi:softmax', 'eval_metric': 'mlogloss', 'num_class': 3, 'verbosity': 0}
xgb_model = xgb.train(xgb_params, xgb_train)
y_pred = xgb_model.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)
#
# print("before:", cal_current_memory())
# model_path = f'{save_dir}/xgb_model_self.bin'
# xgb_model.save_model(model_path)
# print("after", cal_current_memory())
with open(f'{save_dir}/xgb_model_pickle.pkl', 'wb') as f:
    pickle.dump(xgb_model, f)
print(cal_current_memory())
xgb_str = jsonpickle.encode(xgb_model)
with open(f'{save_dir}/xgb_model_jsonpickle.json', 'w') as f:
    f.write(xgb_str)
print(cal_current_memory())


# lgb
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
params = {
    'boosting_type': 'gbdt',
    'objective': 'multiclass',
    'num_class': 3,
    'metric': 'multi_logloss',
    'num_leaves': 31,
    'learning_rate': 0.05,
    'feature_fraction': 0.9
}
gbm = lgb.train(params, lgb_train, num_boost_round=100, valid_sets=[lgb_eval], early_stopping_rounds=5)
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration)
y_pred = [list(x).index(max(x)) for x in y_pred]
lgb_acc = accuracy_score(y_test, y_pred)
#
# print("before:", cal_current_memory())
# model_path = f'{save_dir}/lgb_model_self.bin'
# gbm.save_model(model_path)
# print("after", cal_current_memory())

with open(f'{save_dir}/lgb_model_pickle.pkl', 'wb') as f:
    pickle.dump(gbm, f)

lgb_str = jsonpickle.encode(gbm)
with open(f'{save_dir}/lgb_model_jsonpickle.json', 'w') as f:
    f.write(lgb_str)


# rf
rf = RandomForestClassifier()
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
rf_acc = accuracy_score(y_test, y_pred)


with open(f'{save_dir}/rf_model_pickle.pkl', 'wb') as f:
    pickle.dump(rf, f)

rf_str = jsonpickle.encode(rf)
with open(f'{save_dir}/rf_model_jsonpickle.json', 'w') as f:
    f.write(rf_str)



# catboost
cat_boost_model = CatBoostClassifier(depth=9, learning_rate=0.01,
                                     loss_function='MultiClass', custom_metric=['AUC'],
                                     eval_metric='MultiClass', random_seed=1996)

cat_boost_model.fit(X_train, y_train, eval_set=(X_test, y_test), use_best_model=True, early_stopping_rounds=1000)
y_pred = cat_boost_model.predict(X_test)
cat_acc = accuracy_score(y_test, y_pred)

# t = time.time()
# model_path = f'{save_dir}/cat_boost_model_self.bin'
# cat_boost_model.save_model(model_path)
# print("after", time.time() - t)

# print("before:", cal_current_memory())
# model_path = f'{save_dir}/cat_boost_model_self.bin'
# cat_boost_model.save_model(model_path)
# print("after", cal_current_memory())
with open(f'{save_dir}/cat_boost_model_pickle.pkl', 'wb') as f:
    pickle.dump(cat_boost_model, f)

cat_boost_model_str = jsonpickle.encode(cat_boost_model)
with open(f'{save_dir}/cat_boost_model_jsonpickle.json', 'w') as f:
    f.write(cat_boost_model_str)

print(xgb_acc, lgb_acc, rf_acc, cat_acc)

# 测试

import pickle

import jsonpickle
import psutil
import xgboost as xgb
from catboost import CatBoostClassifier
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
import lightgbm as lgb
from sklearn.ensemble import RandomForestClassifier

save_dir = './models'

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

xgb_test = xgb.DMatrix(X_test, y_test)

xgb_model_self = xgb.Booster()
xgb_model_self.load_model(f'{save_dir}/xgb_model_self.bin')
y_pred = xgb_model_self.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)
print(xgb_acc)

# with open(f'{save_dir}/xgb_model_pickle.pkl', 'rb') as f:
#     xgb_model_pickle = pickle.load(f)
# y_pred = xgb_model_pickle.predict(xgb_test)
# xgb_acc = accuracy_score(y_test, y_pred)
# print(xgb_acc)

#
# with open(f'{save_dir}/xgb_model_jsonpickle.json', 'r') as f:
#     xgb_model_jsonpickle = f.read()
# xgb_model_jsonpickle = jsonpickle.decode(xgb_model_jsonpickle)
# y_pred = xgb_model_jsonpickle.predict(xgb_test)
# xgb_acc = accuracy_score(y_test, y_pred)
# print(xgb_acc)

八、总结

以上实验都是几次实验运行的结果的平均,如果想更有说服力,可以更多次实验取平均值来参考,整体的结果基本上没有差异。(还可以从更大的模型入手来讨论)

  1. 对于图省事,并且想跨平台语言的话可以选择picklejson,但一定要有一定的memory预估,如果模型比较复杂比较大(可能一个模型class包含多种其他模型的对象),会占用非常大的memory,且模型文件也会非常大,但不需要对于每个单独的子模型做序列化,直接decode即可。

  2. 对于要求省空间且运行内存的话,可以选择模型自身的保存方式(主要只保存模型参数文件),但对于这种方式,可能需要在模型的总class去实现序列化和反序列化方法(子模型都要实现,且每个都调用该模型的savemodel和loadmodel方法)

  3. python下不考虑跨平台语言序列化和反序列可以直接考虑pickle的序列化方式,也比较省事。

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

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

相关文章

【操作系统】进程概述

目录 1.进程的概念和定义 2.进程的特征 3.进程的状态 4.进程状态转换 5.进程的构成 5.1进程控制块(PCB) 5.1.1PCB的组织方式 5.2程序段 5.3数据段 6.如何控制进程 6.1进程的创建 6.2进程的终止 6.3进程的阻塞和唤醒 7.进程之间的通信手段 …

OceanBase—02(入门篇——使用obd安装OceanBase的几种方式)

OceanBase—02(入门篇——使用obd安装OceanBase的几种方式) 1. 一键部署1.1 下载并安装 all-in-one 安装包1.2 单机部署 OceanBase 数据库1.2.1 部署命令 1.3 使用 OBClient 客户端连接 OceanBase 数据库1.3.1 两种连接方式1.3.2 遇到的问题1.3.2.1 通过…

Allegro如何设置默认器件的高度信息操作指导

Allegro如何设置默认器件的高度信息操作指导 在给PCB设置限高的时候,一般会添加一个package keepout的铜皮,如下图 如果器件有高度信息,且没有超过限高要求,是不会有DRC报错的,如果器件没有高度信息,软件会默认给匹配一个高度信息,从而导致误报,如下图 可以看到默认的高…

Windows 查看端口占用情况 80端口被占用处理方法

一 . 用netstat指令查看是端口占用情况 查看所有端口的占用情况 C:\Users\Administrator>netstat -ano 活动连接协议 本地地址 外部地址 状态 PIDTCP 0.0.0.0:21 0.0.0.0:0 LISTENING 2228TCP 0.0.0.0:8…

【Leetcode60天带刷】day20二叉树—— 654.最大二叉树 , 617.合并二叉树 , 700.二叉搜索树中的搜索 , 98.验证二叉搜索树

题目: 530. 二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数,其数值等于两值之差的绝对值。 示例 1: 输入:root [4,2,6,1,3] 输出&#xff1…

【工具使用】Gitee

怎么创建Gitee仓库 1. 进入Gitee官网 https://gitee.com/ 2. 没有账号先注册 https://gitee.com/signup?redirect_to_url%2F 3.创建仓库 参考操作页面 你的电脑上要安装git 4. 电脑安装Git 1. 进入git官网(官网下载方法-需要魔法工具) https://…

视觉SLAM十四讲——ch13代码祥读(设计SLAM系统)

视觉SLAM十四讲——ch13学习过程及代码祥读 0. 可以下载文件的网址1. 重读《视觉SLAM十四讲》ch13实践设计SLAM系统2. 主函数的阅读3. config配置文件4. visual_odometry.cpp视觉里程计文件5. frontend.cpp前端文件(重要文件1)6. backend.cpp后端文件&am…

【人工智能概论】 Python标准库——typing(类型标注)、 assert断言、 @property装饰器、setter装饰器

【人工智能概论】 Python标准库——typing(类型标注)、 assert断言、 property装饰器、setter装饰器 文章目录 【人工智能概论】 Python标准库——typing(类型标注)、 assert断言、 property装饰器、setter装饰器一. typing&#…

向量数据库的行业标准逐渐清晰!Vector DB Bench 正式开源!

大模型带火了向量数据库赛道,也让各式各样的向量数据库进入开发者视野。 对于开发者而言,如何选择一个适合自己的向量数据库至关重要。不过,向量数据库的选型并不简单,开发者不仅需要对市面上各种开源和闭源的数据库进行全面的性能…

我心中世界上最好的语言是PHP,别不信【偷笑】

文章目录 一、前言1.1 最适合的语言2.2 流行的语言2.3 Java和PHP2.4 PHP的性能2.5 关于PHP2.6 作文思路概览 二、PHP的优势2.1 跨平台2.2 开发运行环境搭建方便2.3 强大的包管理工具2.4 拥有优秀的代码调试工具xdebug2.5 上手快,学习成本低2.6 丰富的企业级框架2.7 …

集电极开路是什么?集电极开路电路工作原理讲述

​集电极开路是指集电极电路中出现了断路的情况,导致电路无法正常工作。在集电极开路的情况下,电路中的电流无法通过集电极流过,导致电路无法正常放大信号或者控制其他器件的工作。 集电极开路的原因有很多,可能是器件本身的故障…

端午节:传统文化与现代科技的交融

端午节,又称为龙舟节、重五节,是中国传统节日之一。每年农历五月初五,人们会吃粽子、赛龙舟、挂艾草等,以纪念屈原和抵御瘟疫的传说。但是,随着科技的发展,端午节的庆祝方式也在不断地变化和创新。 一、传统…

Selenium 相对定位

目录 前言: 相对定位 工作原理 可用的相对定位 Above Below Left of Right of Near 链式相对定位 相对于WebElement的相对定位 实例演示 前言: Selenium传统定位基本能解决80%的定位需求,但是还是有一些复杂场景传统定位定不到的…

C语言:实现有序序列判断

题目: 输入一个整数序列,判断是否是有序序列,有序,指序列中的整数从小到大排序或者从大到小排序(相同元素也视为有序)。 输入描述: 第一行输入一个整数N (3≤N≤50) 。 第二行输入N个整数,用空格分隔N个整数…

面试滑铁卢,被HR坑惨了......

阎王易见,小鬼难缠。我一直相信这个世界上好人居多,但是也没想到自己也会在阴沟里翻船。我感觉自己被字节跳动的HR坑了。 在这里,我只想告诫大家,offer一定要拿到自己的手里才是真的,口头offer都是不牢靠的&#xff0…

C++学习之详解命名空间

1.理解命名空间 namespace命名空间正如翻译的那样给命名划分空间,每个函数名或变量等都有自己的使用范围,避免的使用函数名冲突的问题; 打个比方,一个班如果有两人叫做张三,那么其中如何区分这两个人那么就得给他们起…

UI自动化测试 | Jenkins配置优化

前一段时间帮助团队搭建了UI自动化环境,这里将Jenkins环境的一些配置分享给大家。 背景: 团队下半年的目标之一是实现自动化测试,这里要吐槽一下,之前开发的测试平台了,最初的目的是用来做接口自动化测试和性能测试&…

如何判断一家公司值不值得去?

通常职场新手都有这样的困惑:收到某某公司的面试(或者offer),我该不该去呢? 为什么有这样的困惑? 大部分原因是因为海投简历导致的。刚出来工作时,我也海投过简历,不管公司好不好&a…

Java使用jna调用c开发的动态库dll文件

文章目录 前言1.c开发动态库dll1.1 新建项目dllDemo1.2 选择C library 在选择shared1.3 项目的目录结构1.4 定义动态库函数1.5 导出动态库 2.java中使用jna调用dll2.1 需要引入jna的依赖2.2 加载动态库dll2.3 测试程序2.3.1 测试代码:2.3.2 测试结果输出 前言 在日…

maven的依赖范围scope使用

测试依赖的范围 #1、依赖范围 标签的位置:dependencies/dependency/scope 标签的可选值:compile/test/provided/system/runtime/import #①compile 和 test 对比 main目录(空间)test目录(空间)开发过程…