ProtoBuf:通讯录4.0实现 序列化能⼒对⽐验证

🌈 个人主页:Zfox_
🔥 系列专栏:ProtoBuf

🔥 ProtoBuf:通讯录4.0实现

Protobuf还常⽤于通讯协议、服务端数据交换场景。那么在这个⽰例中,我们将实现⼀个⽹络版本的通讯录,模拟实现客⼾端与服务端的交互,通过Protobuf来实现各端之间的协议序列化。

需求如下:

  • 客⼾端可以选择对通讯录进⾏以下操作:
    • 新增⼀个联系⼈
    • 删除⼀个联系⼈
    • 查询通讯录列表
    • 查询⼀个联系⼈的详细信息
  • 服务端提供增删查能⼒,并需要持久化通讯录。
  • 客户端、服务端间的交互数据使⽤Protobuf来完成。

本篇博客只对添加联系人进行实现

🦋 环境搭建

Httplib库:cpp-httplib是个开源的库,是⼀个c++封装的http库,使⽤这个库可以在linux、 windows平台下完成http客⼾端、http服务端的搭建。使⽤起来⾮常⽅便,只需要包含头⽂件 httplib.h即可。编译程序时,需要带上-lpthread选项。

源码库地址:https://github.com/yhirose/cpp-httplib

镜像仓库:https://gitcode.net/mirrors/yhirose/cpp-httplib?
utm_source=csdn_github_accelerator

🦋 约定双端交互接⼝

新增⼀个联系⼈:

[请求]Post /contacts/add AddContactRequestContent-Type: application/protobuf[响应]AddContactResponseContent-Type: application/protobuf

删除⼀个联系⼈:

[请求]Post /contacts/del DelContactRequestContent-Type: application/protobuf[响应]DelContactResponseContent-Type: application/protobuf

查询通讯录列表:

[请求]GET /contacts/find-all
[响应]FindAllContactsResponseContent-Type: application/protobuf

查询⼀个联系⼈的详细信息:

[请求]Post /contacts/find-one FindOneContactRequestContent-Type: application/protobuf
[响应]FindOneContactResponseContent-Type: application/protobuf

🦋 约定双端交互 req / resp

base_response.proto

syntax = "proto3";
package base_response;message BaseResponse {bool success = 1; // 返回结果string error_desc = 2; // 错误描述
}

add_contact_request.proto

syntax = "proto3";
package add_contact;message AddContactRequest {string name = 1;int32 age = 2;message Phone {string number = 1;enum PhoneType {MP = 0;TEL = 1;}PhoneType type = 2;}repeated Phone phone = 3;   
}message AddContactResponse {bool success = 1;       // 服务调用是否成功string error_desc = 2;  // 错误原因string uid = 3;
}

add_contact_response.proto

message AddContactResponse {bool success = 1;       // 服务调用是否成功string error_desc = 2;  // 错误原因string uid = 3;
}

del_contact_request.proto

syntax = "proto3";
package del_contact_req;// 删除⼀个联系⼈ req
message DelContactRequest {string uid = 1; // 联系⼈ID
}

del_contact_response.proto

syntax = "proto3";
package del_contact_resp;
import "base_response.proto"; // 引⼊base_response// 删除⼀个联系⼈ resp
message DelContactResponse {base_response.BaseResponse base_resp = 1;string uid = 2;
}

find_one_contact_request.proto

syntax = "proto3";
package find_one_contact_req;// 查询⼀个联系⼈ req
message FindOneContactRequest {string uid = 1; // 联系⼈ID
}

find_one_contact_response.proto

syntax = "proto3";
package find_one_contact_resp;
import "base_response.proto"; // 引⼊base_response// 查询⼀个联系⼈ resp
message FindOneContactResponse {base_response.BaseResponse base_resp = 1;string uid = 2; // 联系⼈IDstring name = 3; // 姓名int32 age = 4; // 年龄message Phone {string number = 1; // 电话号码enum PhoneType {MP = 0; // 移动电话TEL = 1; // 固定电话}PhoneType type = 2; // 类型}repeated Phone phone = 5; // 电话map<string, string> remark = 6; // 备注
}

find_all_contacts_response.proto

syntax = "proto3";
package find_all_contacts_resp;
import "base_response.proto"; // 引⼊base_response// 联系⼈摘要信息
message PeopleInfo {string uid = 1; // 联系⼈IDstring name = 2; // 姓名
}
// 查询所有联系⼈ resp
message FindAllContactsResponse {base_response.BaseResponse base_resp = 1;repeated PeopleInfo contacts = 2;
}

🦋 客户端代码实现

🎀 main.cc

#include <iostream>
#include "httplib.h"
#include "ContactsException.h"
#include "add_contact.pb.h"using namespace httplib;#define CONTACTS_HOST "127.0.0.1"
#define CONTACTS_POST 8080void menu()
{std::cout << "-----------------------------------------------------" << std::endl<< "--------------- 请选择对通讯录的操作 ----------------" << std::endl<< "------------------ 1、新增联系⼈ --------------------" << std::endl<< "------------------ 2、删除联系⼈ --------------------" << std::endl<< "------------------ 3、查看联系⼈列表 ----------------" << std::endl<< "------------------ 4、查看联系⼈详细信息 ------------" << std::endl<< "------------------ 0、退出 --------------------------" << std::endl<< "-----------------------------------------------------" << std::endl;
}void addContact();int main()
{enum OPTION{QUIT = 0,ADD,DEL,FIND_ALL,FIND_ONE};while (true){menu();std::cout << "--->请选择: ";int choose;std::cin >> choose;std::cin.ignore(256, '\n');try{switch (choose){case OPTION::QUIT:std::cout << "--->程序退出" << std::endl;return 0;case OPTION::ADD:addContact();break;case OPTION::DEL:break;case OPTION::FIND_ALL:break;case OPTION::FIND_ONE:break;default:std::cout << "选择有误,请重新选择!" << std::endl;break;}}catch (const ContactsException &e){std::cout << "--->操作通讯录时发生异常" << std::endl;std::cout << "--->异常信息: " << e.what() << std::endl;}}return 0;
}void buildAddContactRequest(add_contact::AddContactRequest *req)
{std::cout << "--------------------新增联系人--------------------" << std::endl;std::cout << "请输入联系人的姓名: ";std::string name;std::getline(std::cin, name);req->set_name(name);std::cout << "请输入联系人的年龄: ";int age;std::cin >> age;req->set_age(age);std::cin.ignore(256, '\n');for (int i = 0;; i++){std::cout << "请输入联系人的电话 " << i + 1 << " " << "(只输⼊回⻋完成电话新增): ";std::string number;std::getline(std::cin, number);if (number.empty())break;add_contact::AddContactRequest_Phone *phone = req->add_phone();phone->set_number(number);std::cout << "请输入该电话的类型 (1. 移动电话   2. 固定电话): ";int type;std::cin >> type;std::cin.ignore(256, '\n');switch (type){case 1:phone->set_type(add_contact::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP);break;case 2:phone->set_type(add_contact::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL);break;default:std::cout << "选择有误! " << std::endl;break;}}std::cout << "-------------------添加联系人成功------------------" << std::endl;
}void addContact()
{Client client(CONTACTS_HOST, CONTACTS_POST);// 构造 reqadd_contact::AddContactRequest req;buildAddContactRequest(&req);// 序列化 reqstd::string req_str;if (!req.SerializeToString(&req_str)){throw ContactsException("AddContactRequest 序列化失败! ");}// 发起 post 调用Result res = client.Post("/contacts/add", req_str, "application/protobuf");if (!res){std::string err_desc;err_desc.append("/contacts/add 链接失败!错误信息:").append(httplib::to_string(res.error()));throw ContactsException(err_desc);}// 反序列化 respadd_contact::AddContactResponse resp;bool parse = resp.ParseFromString(res->body);// 反序列化也失败了if (res->status != 200 && !parse){std::string err_desc;err_desc.append("/contacts/add 调用失败! ").append(std::to_string(res->status)).append("(" + res->reason + ")");throw ContactsException(err_desc);}else if (res->status != 200){std::string err_desc;err_desc.append("/contacts/add 调用失败! ").append(std::to_string(res->status)).append("(" + res->reason + ")").append("错误原因:" + resp.error_desc());throw ContactsException(err_desc);}else if (!resp.success()){std::string err_desc;err_desc.append("/contacts/add 结果异常! ").append(std::to_string(res->status)).append("(" + res->reason + ")").append("异常原因:" + resp.error_desc());throw ContactsException(err_desc);}// 结果打印std::cout << "新增联系人成功,联系人id:" << resp.uid() << std::endl;
}

🎀 ContactException.h:定义异常类

#include <iostream>
#include <string>class ContactsException
{
public:ContactsException(const std::string &str = "A problem") : message(str) {}~ContactsException() {}std::string what() const{return message;}
private:std::string message;
};

🦋 服务端代码实现

🎀 main.cc

#include <iostream>
#include <string>
#include <sstream>
#include <random>
#include <google/protobuf/map.h>
#include "httplib.h"
#include "add_contact.pb.h"using namespace httplib;class ContactsException
{
public:ContactsException(const std::string &str = "A problem") : message(str) {}~ContactsException() {}std::string what() const{return message;}private:std::string message;
};void printContact(add_contact::AddContactRequest &req)
{std::cout << "联系人姓名:" << req.name() << std::endl;std::cout << "联系人年龄: " << req.age() << std::endl;for (int j = 0; j < req.phone_size(); j++){const add_contact::AddContactRequest_Phone &phone = req.phone(j);std::cout << "联系人电话: " << j + 1 << " " << phone.number();std::cout << "  (" << phone.PhoneType_Name(phone.type()) << ")" << std::endl;}
}class Utils
{
public:static unsigned int random_char(){// ⽤于随机数引擎获得随机种⼦std::random_device rd;// mt19937是c++11新特性,它是⼀种随机数算法,⽤法与rand()函数类似,但是mt19937 具有速度快,周期⻓的特点// 作⽤是⽣成伪随机数std::mt19937 gen(rd());// 随机⽣成⼀个整数i 范围[0, 255]std::uniform_int_distribution<> dis(0, 255);return dis(gen);}// ⽣成 UUID (通⽤唯⼀标识符)static std::string generate_hex(const unsigned int len){std::stringstream ss;// ⽣成 len 个16进制随机数,将其拼接⽽成for (auto i = 0; i < len; i++){const auto rc = random_char();std::stringstream hexstream;hexstream << std::hex << rc;auto hex = hexstream.str();ss << (hex.length() < 2 ? '0' + hex : hex);}return ss.str();}static void map_copy(google::protobuf::Map<std::string, std::string> *target, const google::protobuf::Map<std::string, std::string> &source){if (nullptr == target){std::cout << "map_copy warning, target is nullptr!" << std::endl;return;}for (auto it = source.cbegin(); it != source.cend(); ++it){target->insert({it->first, it->second});}}
};int main()
{std::cout << "-------------服务启动-------------" << std::endl;Server server;server.Post("/contacts/add", [](const Request &req, Response &resp) {std::cout << "接收到post请求!" << std::endl;// 反序列化 request req.bodyadd_contact::AddContactRequest request;add_contact::AddContactResponse response;try {if(!request.ParseFromString(req.body)) {throw ContactsException("AddContactRequest 反序列化失败! ");}// 新增联系人,持久化存储通讯录  ----> 打印新增的联系人信息printContact(request);// 构造 response resp.bodyresponse.set_success(true);response.set_uid(Utils::generate_hex(10));// resp.body(序列化 response)std::string response_str;if(!response.SerializeToString(&response_str)) {throw ContactsException("AddContactResponse 序列化失败");}resp.status = 200;resp.body = response_str;resp.set_header("Content-Type", "application/protobuf");} catch (const ContactsException &e) {resp.status = 500;response.set_success(false);response.set_error_desc(e.what());std::string response_str;if(response.SerializeToString(&response_str)) {resp.body = response_str;resp.set_header("Content-Type", "application/protobuf");}std::cout << "/contacts/add 发生异常,异常信息:" << e.what() << std::endl;} });// 绑定 8080 端口,并且将端口对外开放server.listen("0.0.0.0", 8080);return 0;
}

🔥 序列化能⼒对⽐验证

在这⾥让我们分别使⽤PB与JSON的序列化与反序列化能⼒,对值完全相同的⼀份结构化数据进⾏不同次数的性能测试。

为了可读性,下⾯这⼀份⽂本使⽤JSON格式展⽰了需要被进⾏测试的结构化数据内容:

{"age" : 20,"name" : "张珊","phone" :{{"number" : "110112119","type" : 0},{"number" : "110112119","type" : 0},{"number" : "110112119","type" : 0},{"number" : "110112119","type" : 0},{"number" : "110112119","type" : 0}},"qq" : "95991122","address" :{"home_address" : "陕西省西安市⻓安区","unit_address" : "陕西省西安市雁塔区"},"remark" :{"key1" : "value1","key2" : "value2","key3" : "value3","key4" : "value4","key5" : "value5"}
}

开始进⾏测试代码编写,我们在新的⽬录下新建contacts.proto⽂件,内容如下:

syntax = "proto3";
package compare_serialization;
import "google/protobuf/any.proto"; // 引⼊ any.proto ⽂件// 地址
message Address{string home_address = 1; // 家庭地址string unit_address = 2; // 单位地址
}// 联系⼈
message PeopleInfo {string name = 1; // 姓名int32 age = 2; // 年龄message Phone {string number = 1; // 电话号码enum PhoneType {MP = 0; // 移动电话TEL = 1; // 固定电话}PhoneType type = 2; // 类型}repeated Phone phone = 3; // 电话google.protobuf.Any data = 4;oneof other_contact { // 其他联系⽅式:多选⼀string qq = 5;string weixin = 6;}map<string, string> remark = 7; // 备注
}

使⽤protoc命令编译⽂件后,新建性能测试⽂件compare.cc,我们分别对相同的结构化数据进⾏ 100 、 1000 、 10000 、 100000 次的序列化与反序列化,分别获取其耗时与序列化后的⼤⼩。

内容如下:

#include <iostream>
#include <sys/time.h>
#include <jsoncpp/json/json.h>
#include "contacts.pb.h"using namespace std;
using namespace compare_serialization;
using namespace google::protobuf;#define TEST_COUNT 100void createPeopleInfoFromPb(PeopleInfo *people_info_ptr);
void createPeopleInfoFromJson(Json::Value &root);int main(int argc, char *argv[])
{struct timeval t_start, t_end;double time_used;int count;string pb_str, json_str;// ------------------------------Protobuf 序列化------------------------------------{PeopleInfo pb_people;createPeopleInfoFromPb(&pb_people);count = TEST_COUNT;gettimeofday(&t_start, NULL);// 序列化count次while ((count--) > 0){pb_people.SerializeToString(&pb_str);}gettimeofday(&t_end, NULL);time_used = 1000000 * (t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec -t_start.tv_usec;cout << TEST_COUNT << "次 [pb序列化]耗时:" << time_used / 1000 << "ms."<< " 序列化后的⼤⼩:" << pb_str.length() << endl;}// ------------------------------Protobuf 反序列化------------------------------------{PeopleInfo pb_people;count = TEST_COUNT;gettimeofday(&t_start, NULL);// 反序列化count次while ((count--) > 0){pb_people.ParseFromString(pb_str);}gettimeofday(&t_end, NULL);time_used = 1000000 * (t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec -t_start.tv_usec;cout << TEST_COUNT << "次 [pb反序列化]耗时:" << time_used / 1000 << "ms."<< endl;}// ------------------------------JSON 序列化------------------------------------{Json::Value json_people;createPeopleInfoFromJson(json_people);Json::StreamWriterBuilder builder;count = TEST_COUNT;gettimeofday(&t_start, NULL);// 序列化count次while ((count--) > 0){json_str = Json::writeString(builder, json_people);}gettimeofday(&t_end, NULL);// 打印序列化结果// cout << "json: " << endl << json_str << endl;time_used = 1000000 * (t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec -t_start.tv_usec;cout << TEST_COUNT << "次 [json序列化]耗时:" << time_used / 1000 << "ms."<< " 序列化后的⼤⼩:" << json_str.length() << endl;}// ------------------------------JSON 反序列化------------------------------------{Json::CharReaderBuilder builder;unique_ptr<Json::CharReader> reader(builder.newCharReader());Json::Value json_people;count = TEST_COUNT;gettimeofday(&t_start, NULL);// 反序列化count次while ((count--) > 0){reader->parse(json_str.c_str(), json_str.c_str() + json_str.length(),&json_people, nullptr);}gettimeofday(&t_end, NULL);time_used = 1000000 * (t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec -t_start.tv_usec;cout << TEST_COUNT << "次 [json反序列化]耗时:" << time_used / 1000 << "ms."<< endl;}return 0;
}/*** 构造pb对象*/
void createPeopleInfoFromPb(PeopleInfo *people_info_ptr)
{people_info_ptr->set_name("张珊");people_info_ptr->set_age(20);people_info_ptr->set_qq("95991122");for (int i = 0; i < 5; i++){PeopleInfo_Phone *phone = people_info_ptr->add_phone();phone->set_number("110112119");phone->set_type(PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP);}Address address;address.set_home_address("陕西省西安市⻓安区");address.set_unit_address("陕西省西安市雁塔区");google::protobuf::Any *data = people_info_ptr->mutable_data();data->PackFrom(address);people_info_ptr->mutable_remark()->insert({"key1", "value1"});people_info_ptr->mutable_remark()->insert({"key2", "value2"});people_info_ptr->mutable_remark()->insert({"key3", "value3"});people_info_ptr->mutable_remark()->insert({"key4", "value4"});people_info_ptr->mutable_remark()->insert({"key5", "value5"});
}/*** 构造json对象*/
void createPeopleInfoFromJson(Json::Value &root)
{root["name"] = "张珊";root["age"] = 20;root["qq"] = "95991122";for (int i = 0; i < 5; i++){Json::Value phone;phone["number"] = "110112119";phone["type"] = 0;root["phone"].append(phone);}Json::Value address;address["home_address"] = "陕西省西安市⻓安区";address["unit_address"] = "陕西省西安市雁塔区";root["address"] = address;Json::Value remark;remark["key1"] = "value1";remark["key2"] = "value2";remark["key3"] = "value3";remark["key4"] = "value4";remark["key5"] = "value5";root["remark"] = remark;
}

测试结果如下:

100[pb序列化]耗时:0.342ms. 序列化后的⼤⼩:278
100[pb反序列化]耗时:0.435ms.
100[json序列化]耗时:1.306ms. 序列化后的⼤⼩:567
100[json反序列化]耗时:0.926ms.1000[pb序列化]耗时:3.59ms. 序列化后的⼤⼩:278
1000[pb反序列化]耗时:5.069ms.
1000[json序列化]耗时:11.582ms. 序列化后的⼤⼩:567
1000[json反序列化]耗时:9.289ms.10000[pb序列化]耗时:34.386ms. 序列化后的⼤⼩:278
10000[pb反序列化]耗时:45.96ms.
10000[json序列化]耗时:115.76ms. 序列化后的⼤⼩:567
10000[json反序列化]耗时:91.046ms.100000[pb序列化]耗时:349.937ms. 序列化后的⼤⼩:278
100000[pb反序列化]耗时:428.366ms.
100000[json序列化]耗时:1150.54ms. 序列化后的⼤⼩:567
100000[json反序列化]耗时:904.58ms.

由实验结果可得:

  • 编解码性能:ProtoBuf的编码解码性能,⽐JSON⾼出2-4倍。
  • 内存占⽤:ProtoBuf的内存278,⽽JSON到达567,ProtoBuf的内存占⽤只有JSON的1/2。

注:以上结论的数据只是根据该项实验得出。因为受不同的字段类型、字段个数等影响,测出的数据会有所差异。

该实验有很多可待优化的地⽅。但其实这种粗略的测试,也能看出来ProtoBuf的优势。

🦋 总结

在这里插入图片描述

⼩结:

  1. XML、JSON、ProtoBuf都具有数据结构化和数据序列化的能⼒。
  2. XML、JSON更注重数据结构化,关注可读性和语义表达能⼒。ProtoBuf更注重数据序列化,关注效率、空间、速度,可读性差,语义表达能⼒不⾜,为保证极致的效率,会舍弃⼀部分元信息。
  3. ProtoBuf的应⽤场景更为明确,XML、JSON的应⽤场景更为丰富。

🔥 共勉

😋 以上就是我对 ProtoBuf:通讯录4.0实现 & 序列化能⼒对⽐验证 的理解, 觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~ 😉
在这里插入图片描述

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

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

相关文章

界面控件DevExpress WPF v24.2新版亮点:电子表格组件全新升级

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 DevExpress WPF控件近…

EcoVadis提升评估得分的策略,EcoVadis常见挑战与解决方案

EcoVadis评估概述 EcoVadis是全球领先的企业社会责任(CSR)评级平台&#xff0c;为全球供应链提供可持续性评估服务。该评估体系通过对环境、劳工与人权、商业道德和可持续采购四大主题的全面评估&#xff0c;帮助企业衡量和改进其CSR表现。 评估核心内容 EcoVadis评估涵盖以…

深入理解指针(五)

1. 回调函数是什么&#xff1f; 2. qsort使用举例 3. qsort函数的模拟实现 1. 回调函数是什么&#xff1f; 回调函数就是⼀个通过函数指针调用的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另⼀个函数&#xff0c;当这个指针被用来调用其所指向的…

Docker 日志

Docker 日志是排查容器故障、监控运行状态的重要工具。下面从 日志查看命令、详解字段、日志驱动、最佳实践 四个方面给你详细解析。 一、最常用日志命令 1. 查看容器日志&#xff08;默认 stdout、stderr&#xff09; docker logs <container_name|container_id>2. 实…

SAP生产环境修改程序

1. 关键的两个标准函数 TRINT_CORR_INSERT TRINT_CORR_CHECK 2. 自定义SAP生产环境修改程序 *data:begin of itab occurs 0, * lines(150), * end of itab. DATA itab TYPE TABLE OF string. PARAMETERS:program LIKE rs38m-programm. READ REPORT program INT…

构建高性能网络服务:从Reactor模式到现代服务器架构设计

在当今高并发、低延迟的应用场景下&#xff0c;如何设计高效稳定的网络服务成为后端开发的核心挑战。本文将深入探讨网络服务的演进路径&#xff0c;结合Reactor模式、one thread one loop思想等关键技术&#xff0c;揭示高性能服务器架构的设计精髓。 一、网络通信的核心问题与…

HarmonyOS 5 多端适配原理与BreakpointSystem工具类解析:附代码

H 一、鸿蒙多端适配的核心概念 鸿蒙系统的多端适配通过响应式布局和媒体查询实现&#xff0c;核心在于根据设备屏幕尺寸动态调整UI结构。其实现逻辑与Web响应式设计类似&#xff0c;但针对鸿蒙ArkUI框架进行了定制化封装。 二、BreakpointSystem工具类&#xff1a;多端适配的…

Telerik生态整合:Kendo UI for Angular组件在WinForms应用中的深度嵌入(二)

Telerik DevCraft包含一个完整的产品栈来构建您下一个Web、移动和桌面应用程序。它使用HTML和每个.NET平台的UI库&#xff0c;加快开发速度。Telerik DevCraft提供完整的工具箱&#xff0c;用于构建现代和面向未来的业务应用程序&#xff0c;目前提供UI for ASP.NET MVC、Kendo…

红帽全球副总裁曹衡康:开源AI开启企业级应用新纪元

在生成式AI技术迅猛发展的今天&#xff0c;ChatGPT、DeepSeek、元宝等AI应用已不再仅仅是科技前沿的象征&#xff0c;而是切实地融入到了我们的工作与生活之中&#xff0c;为企业带来了前所未有的变革机遇。对于企业而言&#xff0c;如何有效利用AI技术降本增效&#xff0c;已成…

异构计算解决方案(兼容不同硬件架构)

异构计算解决方案通过整合不同类型处理器&#xff08;如CPU、GPU、NPU、FPGA等&#xff09;&#xff0c;实现硬件资源的高效协同与兼容&#xff0c;满足多样化计算需求。其核心技术与实践方案如下&#xff1a; 一、硬件架构设计 异构处理器组合‌ 主从协作模式‌&#xff1a…

中科米堆汽车车门自动化三维检测3D尺寸测量设备自动外观检测

汽车的每一个零部件的质量都关乎着整车的性能与安全。汽车车门作为车辆的重要组成部分&#xff0c;不仅承担着保护车内人员安全的关键职责&#xff0c;其外观质量与尺寸精度也直接影响着消费者的第一印象和驾驶体验。 汽车车门制造涉及众多复杂的工艺流程&#xff0c;从冲压成…

Python 数据分析与可视化 Day 4 - Pandas 数据筛选与排序操作

&#x1f3af; 今日目标 掌握 Pandas 中 groupby() 的使用方式学会使用 agg() 方法进行多个聚合掌握 pivot_table() 构建透视表结合分组与排序进行更深入的分析 &#x1f9ee; 一、基本分组统计&#xff08;groupby&#xff09; ✅ 分组 单列聚合 df.groupby("性别&qu…

智能营销系统对企业的应用价值

在当前快速迭代的商业环境中&#xff0c;企业与客户的连接方式正经历前所未有的深刻变革。传统的市场策略在数据洪流和日益个性化的消费者需求面前&#xff0c;效能正逐步递减。 企业决策者普遍面临一个核心挑战&#xff1a;如何在复杂多变的市场中&#xff0c;实现营销资源的最…

docker镜像中集成act工具

# 使用官方 Ubuntu 22.04 基础镜像 FROM ubuntu:22.04# 安装系统依赖并清理缓存 RUN apt-get update && \apt-get install -y --no-install-recommends \curl \git \make \gcc \g \libssl-dev \pkg-config \&& \apt-get clean && \rm -rf /var/lib/apt…

Docker 与 Containerd 交互机制简单剖析

#作者&#xff1a;邓伟 文章目录 一、背景&#xff1a;Docker 架构的演进之路1.1 从自研运行时到 OCI 标准化1.2 现行架构分层模型 二、核心交互组件解析2.1 通信协议&#xff1a;gRPC 双向流的应用2.2 镜像生命周期管理交互2.2.1 镜像拉取流程&#xff08;以 docker pull 为例…

C++ Vector 基础入门操作

一、Vector初始化&#xff1a;5种常用方式 ​​1. 默认构造​​ 创建空容器&#xff0c;适用于后续动态添加元素&#xff1a; std::vector<int> vec; // 空vector&#xff0c;size0 2. 指定大小和初值​​ 预分配空间并初始化元素&#xff1a; std::vector<int>…

社会治理创新平台PPT(48页)

社会治理创新背景 社会治理创新旨在加强和完善基层社会管理和服务体系&#xff0c;提升政府效能&#xff0c;强化城乡社区自治和服务功能。自党的十六届四中全会提出“推进社会管理体制创新”以来&#xff0c;社会治理创新已成为政府工作的重要篇章。 社会治理创新现状与挑战…

论文笔记:Answering POI-Recommendation Questions using TourismReviews

2021 CIKM 1 intro 根据贝恩公司&#xff08;Bain & Company&#xff09;2019年的一份报告&#xff0c;旅行者在预订前通常会进行33至500次网页搜索 部分用户会访问超过50个旅游网站&#xff0c;三分之一的上网时间都用于与旅行相关的活动。在某些情况下&#xf…

带约束的高斯牛顿法求解多音信号分离问题

一、信号模型与优化问题建立 1. 复信号模型 设观测的复信号由两个单频复指数信号加噪声组成&#xff1a; x [ n ] A 0 e j ( 2 π f 0 n T s ϕ 0 ) A 1 e j ( 2 π f 1 n T s ϕ 1 ) w [ n ] , n 0 , 1 , … , N − 1 x[n] A_0 e^{j(2\pi f_0 n T_s \phi_0)} A_1 e…

Java并发编程中高效缓存设计的哲学

文章目录 引言详解缓存的设计和演进基于缓存存储运算结果锁分段散列减小锁粒度异步化提升处理效率原子化避免重复运算小结参考引言 本文将基于并发编程和算法中经典的哈希取模、锁分段、 异步化、原子化。这几个核心设计理念编写逐步推演出一个相对高效的缓存工具,希望对你有…