当前位置: 首页 > news >正文

C++11使用多线程(线程池)计算相似度实现性能优化

需求:图像识别中,注册的样本多了会影响计算速度,成为性能瓶颈,其中一个优化方法就是使用多线程。例如,注册了了3000个特征,每个特征4096个float。可以把3000个特征比对放到4个线程中进行计算,然后再把结果进行合并。实现思路:

1. 根据系统性能和需求配置线程池的大小,创建线程池,将比较任务平均分配到各个线程

2. 工作线程启动后在一个condition_variable上wait,注意:锁的范围不能太大了,否则多个线程会变成串行

3. 调用者调用识别接口,接口更新目标特征,通知各个工作线程,在另外一个condition_variable上wait,并且满足完成计数器的值等于线程数

4. 工作线程完成后将计数器加一,并且通知调用线程

5. 调用线程收集到所有线程的结果后再对结果进行合并返回

后续:

1. 代码进行优化,更优雅的实现

测试结果:

线程数

时间

1

71362ms

2

36292ms

4

19420ms

8

18465ms

16

18433ms

32

18842ms

64

19324ms

128

19388ms

256

21853ms

512

26150ms

1024

35593ms

代码如下:

#include <iostream>

#include <string>

#include <cstring>

#include <mutex>

#include <unordered_map>

#include <list>

#include <utility>

#include <algorithm>

#include <string>

#include <vector>

#include <thread>

#include <chrono>

using namespace std;

using namespace chrono;


 

double get_mold(const vector<double> &vec)

{

int n = vec.size();

double sum = 0.0;

for(int i = 0; i < n; ++i)

{

sum += vec[i] * vec[i];

}

return sqrt(sum);

}

double cosine_distance(const vector<double> &base, const vector<double> &target)

{

int n = base.size();

double tmp = 0.0;

for(int i = 0; i < n; ++i)

{

tmp += base[i] * target[i];

}

double simility = tmp / (get_mold(base) * get_mold(target));

return simility;

}


 

class Recognizer

{

public:

Recognizer(int num_threads) :

num_threads_(num_threads),

is_run_calculate_thread_(true),

is_doing_recognize(false),

result_count(0)

{

recognize_result = std::vector<RecognizeResult>(num_threads);

this->load_feature();

this->init_threads();

}

~Recognizer()

{

is_run_calculate_thread_ = false;

cv_.notify_all();

for(std::thread &th : threads_)

th.join();

}

int do_recognize(const vector<double> &feature);

private:

class CigaretteItem

{

public:

int cigarette_id_;

std::string cigarette_name_;

std::vector<double> feature_;

CigaretteItem(int cigarette_id, std::string cigarette_name, const std::vector<double> &cigarette_feature)

{

cigarette_id_ = cigarette_id;

cigarette_name_ = cigarette_name;

feature_ = std::vector<double>(cigarette_feature.size());

for(int i = 0; i < cigarette_feature.size(); i++)

{

feature_[i] = cigarette_feature[i];

}

}

};

class RecognizeResult

{

public:

int cigarette_id_;

std::string cigarette_name_;

double score_;

};

private:

int num_threads_;

bool is_run_calculate_thread_;

bool is_doing_recognize;

std::vector<CigaretteItem> ciagarette_list_;

std::vector<double> target_feature_;

std::mutex cv_mtx_;

std::condition_variable cv_;

std::vector<RecognizeResult> recognize_result;

std::vector<std::thread> threads_;

int result_count;

std::mutex result_count_mtx_;

std::mutex result_cv_mtx_;

std::condition_variable result_cv_;


 

private:

Recognizer(const Recognizer&) = delete;

Recognizer& operator=(const Recognizer&) = delete;

void load_feature();

void init_threads();

void calculate_most_similarity(const int thread_id, const int start_index, const int end_index);

};


 

void Recognizer::load_feature()

{

for(int i = 0; i < 3000; i++)

{

vector<double> fea = vector<double>(4096);

for(int i = 0; i < 4096; ++i)

fea[i] = (double)(rand() % 998 + 1) / 1000.00;

ciagarette_list_.emplace_back(i+1, "cigarette", fea);

}

}

void Recognizer::init_threads()

{

for(int i = 0; i < num_threads_; i++)

{

int step = this->ciagarette_list_.size() / this->num_threads_;

int start_index = i * step;

int end_index = (i+1) * step;

if(i == num_threads_ - 1){

end_index = ciagarette_list_.size();

}

std::cout << "thread" << i << " starts at " << start_index << "; ends at " << end_index << std::endl;

threads_.emplace_back(&Recognizer::calculate_most_similarity, this, i, start_index, end_index);

}

}

void Recognizer::calculate_most_similarity(const int thread_id, const int start_index, const int end_index)

{

while(is_run_calculate_thread_)

{

{

std::unique_lock<std::mutex> lock(cv_mtx_);

cv_.wait(lock);

}

//cout << "thread" << thread_id << " is running" << endl;

double max_score = -1.00;

int max_score_index = -1;

for(int i = start_index; i < end_index; ++i){

double score = cosine_distance(ciagarette_list_[i].feature_, target_feature_);

if(score > max_score)

{

max_score = score;

max_score_index = i;

}

}

recognize_result[thread_id].cigarette_id_ = ciagarette_list_[max_score_index].cigarette_id_;

recognize_result[thread_id].cigarette_name_ = ciagarette_list_[max_score_index].cigarette_name_;

recognize_result[thread_id].score_ = max_score;

{

std::unique_lock<std::mutex> lock(result_count_mtx_);

result_count += 1;

}

result_cv_.notify_one();

//std::cout << "thread" << thread_id << " finish one task" << endl;

}

//std::cout << "thread" << thread_id << " finished." << std::endl;

}

int Recognizer::do_recognize(const vector<double> &feature)

{

if(is_doing_recognize)

return -1;

is_doing_recognize = true;

this->target_feature_ = feature;

//cout << "cv_.notify_all()" << endl;

cv_.notify_all();

std::unique_lock<std::mutex> lock(result_cv_mtx_);

result_cv_.wait(lock, [this](){return this->num_threads_ == this->result_count;});

//std::cout << "all threads finish computing similarity" << endl;

int max_score_cigarette_id = -1;

std::string max_score_cigarette_name = "";

double max_score = -1.0;

for(int i = 0; i < num_threads_; ++i)

{

if(recognize_result[i].score_ > max_score)

{

max_score_cigarette_id = recognize_result[i].cigarette_id_;

max_score_cigarette_name = recognize_result[i].cigarette_name_;

max_score = recognize_result[i].score_;

}

}

//cout << "cigarette_id=" << max_score_cigarette_id << ", cigarette_name=" << max_score_cigarette_name << ", score=" << max_score << endl;

this->result_count = 0;

is_doing_recognize = false;

return 0;

}


 

int main(void)

{

Recognizer recognizer{1024};

std::this_thread::sleep_for(std::chrono::seconds(1));

const int loops = 400;

auto start_time = system_clock::now();

for(int i = 0; i < loops; i++)

{

//cout << endl;

std::vector<double> target_feature = std::vector<double>(4096);

for(int i = 0; i < 4096; ++i)

{

//target_feature[i] = (double)(rand() % 998 + 1) / 1000.000;

target_feature[i] = (double)(i % 1000 + 1) / 1000.00;

}

recognizer.do_recognize(target_feature);

//if((i+1) % 20 == 0)

// cout << "i=" << i << endl;

}

auto end_time = system_clock::now();

auto duration = duration_cast<milliseconds>(end_time - start_time);

cout << "eplased_time:" << duration.count() << "ms" << endl;

std::this_thread::sleep_for(std::chrono::seconds(2));

return 0;

}

相关文章:

C++11使用多线程(线程池)计算相似度实现性能优化

需求&#xff1a;图像识别中&#xff0c;注册的样本多了会影响计算速度&#xff0c;成为性能瓶颈&#xff0c;其中一个优化方法就是使用多线程。例如&#xff0c;注册了了3000个特征&#xff0c;每个特征4096个float。可以把3000个特征比对放到4个线程中进行计算&#xff0c;然…...

【测绘程序设计】——平面坐标转换

测绘工程中经常遇到平面坐标转换——比如,北京54(或西安80)平面坐标转换成CGCS2000平面坐标、工程独立坐标系平面坐标转换成CGCS2000平面坐标等,常用转换模型包括:①三参数法(2平移+1旋转);②四参数法(赫尔默特法,2平移+1旋转+1尺度);③六参数法(仿射变换法,2平移…...

五子棋的设计与实现

术&#xff1a;Java等摘要&#xff1a;五子棋是一种两人对弈的纯策略型棋类游戏&#xff0c;非常容易上手&#xff0c;老少皆宜。为了更好的推广五子棋&#xff0c;研究简单的人工智能方式&#xff0c;运用Java开发五子棋游戏。主要包含了人机对战&#xff0c;棋盘初始化&#…...

大数据项目软硬件选择

目录 一.技术选型 二.系统数据流程设计 三.框架版本选型 如何选择Apache/CDH/HDP版本...

redis数据结构的适用场景分析

1、String 类型的内存空间消耗问题&#xff0c;以及选择节省内存开销的数据类型的解决方案。 为什么 String 类型内存开销大&#xff1f; 图片 ID 和图片存储对象 ID 都是 10 位数&#xff0c;我们可以用两个 8 字节的 Long 类型表示这两个 ID。因为 8 字节的 Long 类型最大可以…...

同步、异步、全双工、半双工的区别

1、通讯 1.1 并行通讯 定义&#xff1a;一条信息的各位数据被同时传送的通讯方式称为并行通讯&#xff1b; 特点&#xff1a; 各个数据位同时发送&#xff0c;传送速度快、效率高&#xff0c;但有多少数据位就需要多少根数据线&#xff0c;因此传送成本高&#xff0c;并且只…...

ClickHouse 与 Amazon S3 结合?一起来探索其中奥秘

目录ClickHouse 简介ClickHouse 与对象存储ClickHouse 与 S3 结合的三种方法示例参考架构小结参考资料ClickHouse 简介ClickHouse 是一种快速的、开源的、用于联机分析&#xff08;OLAP&#xff09;的列式数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;由俄罗斯的Yan…...

【Spark分布式内存计算框架——Structured Streaming】1. Structured Streaming 概述

前言 Apache Spark在2016年的时候启动了Structured Streaming项目&#xff0c;一个基于Spark SQL的全新流计算引擎Structured Streaming&#xff0c;让用户像编写批处理程序一样简单地编写高性能的流处理程序。 Structured Streaming并不是对Spark Streaming的简单改进&#xf…...

【Windows】【Linux】---- Java证书导入

问题&#xff1a; PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 无法找到请求目标的有效证书路径 一、Windows—java证书导入 1、下载证书到本地&#xff08;以下…...

【Linux学习】菜鸟入门——gcc与g++简要使用

一、gcc/g gcc/g是编译器&#xff0c;gcc是GCC(GUN Compiler Collection&#xff0c;GUN编译器集合)中的C编译器&#xff1b;g是GCC中的C编译器。使用g编译文件时会自动链接STL标准库&#xff0c;而gcc不会自动链接STL标准库。下面简单介绍一下Linux环境下&#xff08;Windows差…...

Cadence Allegro 导出Bill of Material Report详解

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,Assigned Functions Report作用3,Assigned Functions Report示例4,Assigned Functions Report导出方法4.1,方法14.2,方法2B站关注“硬小二”浏览更多演示视频...

localStorage线上问题的思考

一、背景&#xff1a; localStorage作为HTML5 Web Storage的API之一&#xff0c;使用标准的键值对&#xff08;Key-Value,简称KV&#xff09;数据类型主要作用是本地存储。本地存储是指将数据按照键值对的方式保存在客户端计算机中&#xff0c;直到用户或者脚本主动清除数据&a…...

什么是DNS域名解析

什么是DNS域名解析&#xff1f;因特网上作为域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使用户更方便的访问互联网&#xff0c;而不用去记住能够被机器直接读取的IP数串。通过主机名&#xff0c;得到该主机名对应的IP地址的过程叫做域名解析。正向解析&#xff1a…...

Cadence Allegro 导出Assigned Functions Report详解

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,Assigned Functions Report作用3,Assigned Functions Report示例4,Assigned Functions Report导出方法4.1,方法14.2,方法2B站关注“硬小二”浏览更多演示视频...

Python中Opencv和PIL.Image读取图片的差异对比

近日&#xff0c;在进行深度学习进行推理的时候&#xff0c;发现不管怎么样都得不出正确的结果&#xff0c;再仔细和正确的代码进行对比了后发现原来是Python中不同的库读取的图片数组是有差异的。 image np.array(Image.open(image_file).convert(RGB)) image cv2.imread(…...

win10 WSL2 使用Ubuntu配置与安装教程

Win10 22H2ubuntu 22.04ROS2 文章目录一、什么是WSL2二、Win10 系统配置2.1 更新Windows版本2.2 Win10系统启用两个功能2.3 Win10开启BIOS/CPU开启虚拟化(VT)&#xff08;很关键&#xff09;2.4 下载并安装wsl_update_x64.msi2.5 PowerShell安装组件三、PowerShell安装Ubuntu3.…...

LeetCode每日一题(28. Find the Index of the First Occurrence in a String)

Given two strings needle and haystack, return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack. Example 1: Input: haystack “sadbutsad”, needle “sad” Output: 0 Explanation: “sad” occurs at index 0 and…...

Android 圆弧形 SeekBar

效果预览package com.gcssloop.widget;import android.annotation.SuppressLint;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Matrix;import android.graph…...

java 字典

java 字典 数据结构总览 Map Map 描述的是一种映射关系&#xff0c;一个 key 对应一个 value&#xff0c;可以添加&#xff0c;删除&#xff0c;修改和获取 key/value&#xff0c;util 提供了多种 Map HashMap: hash 表实现的 map&#xff0c;插入删除查找性能都是 O(1)&…...

【企业服务器LNMP环境搭建】mysql安装

MySQL安装步骤&#xff1a; 1、相关说明 1.1、编译参数的说明 -DCMAKE_INSTALL_PREFIX安装到的软件目录-DMYSQL_DATADIR数据文件存储的路径-DSYSCONFDIR配置文件路径 (my.cnf)-DENABLED_LOCAL_INFILE1使用localmysql客户端的配置-DWITH_PARTITION_STORAGE_ENGINE使mysql支持…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...