`

并发内存分配问题以及TBB的解决方案

阅读更多

在多线程程序中,普通的内存分配将成为严重的性能瓶颈。本文介绍了怎样使用Threading Building Blocks的可扩展内存分配器来避免内存分配竞争和假共享问题。

内存分配不仅是编程的基本任务,也是在多核编程时影响效率的一大挑战。在C++里我们可以用自定义内存分配器代替std:: allocator,Threading Building Blocks就提供了一个与std::allocator兼容的可扩展内存分配器。

每个内存分配器都有自己的特点。TBB的可扩展分配器致力于可扩展性和速度。在某些情况下,这是有代价的:浪费虚拟空间。具体来说,当 分配9k到12k的块可它会浪费较大的空间。

内存分配问题

在多线程程序中,普通的内存分配将成为严重的性能瓶颈。这是因为普通的内存分配器用一个全局锁从一个单一的全局堆中分配和释放内存块, 每个线程分配和释放内存时就会引发竞争。正是由于这种竞争,频繁内存分配的程序可能降低多核带来的优势。使用C++标准库(STL)的程序可能会更为严 重,因为它们的内存分配往往隐藏在背后。

这里先放一段代码,它并发地执行一个有内存申请和释放操作的函数:

#include <iostream>
#include <tbb/parallel_for.h>
#include <tbb/tick_count.h>
#include <vector>

using namespace tbb;

void alloctask( int )
{ 
 //vector构造和析构时会申请和释放空间
 std::vector<int> data(100);
}

int main()
{
 tick_count t1 = tick_count::now(); //用于记录花费的时间
 parallel_for(0,100000,1,alloctask); //十万次执行alloctask(并发)
 tick_count t2 = tick_count::now();

 std::cout << (t2-t1).seconds() << std::endl;
 return 0;
}

你可以运行这段代码查看所花费的时间,我们可以很容易地把上面的代码修改成使用TBB可扩展内存分配器,这样速度会加快将近一倍(在偶 双核CPU上,预计更多内核的CPU会有更好的表现)。

注:如果之前看过本站关于TBB循环的 文章的朋友可能会奇怪,这里没有task_scheduler_init,而且parallel_for的参数也不一样。其实这段代码是基于最新的TBB2.2版 本的,这个版本已经不再强制要求task_scheduler_init。parallel_for也多了几个重载以方便使用。

“假共享”是并发程序中另一个严重问题,它常发生于当多个线程使用的内存块紧靠在一起时。在处理器内核中有一个称为“cache lines”的高速缓存区,它只能由同一个线程存取同一个缓存区,否则就会引发缓存切换,这可以轻易地造成上百时钟周期的浪费。

为了说明为什么假共享有这样的性能损失,我们可以看看当两个线程访问紧靠在一起的内存时引起的额外的开销。假设这个缓存区有64字节, 两个线程共享这个缓存。

首先,程序定义了两个数组,包含1000个float(4字节):

float A_array [1000];
float B_array [1000];

由于编译器的顺序分配,这两个数组很可能是紧靠在一起的。考虑下面的动作:

  1. 线程A写入A_array[999];
    处理器将包含A_array[999]这个元素的64个字节放入缓存
  2. 线程B写入B_array [0];
    额外的开销:处理器必须刷新缓存,把A_array[999]保存到内存中。把包含B_array[0]的64个字节载入缓存并设置线程A的缓存 标记为无效。
  3. 继续工作,线程A写入A_array[1];
    额外的开销:处理器必须刷新缓存,以便把B_array [0]保存到内存中。重新为线程A加载缓存并设置线程B的缓存标记为无效。

看,即使线程A和线程B使用各自的内存还是会造成极大的开销。解决方法假共享的办法是把数组按缓存边界对齐。

内存分配器

TBB的可扩展内存分配器可以用来解决上面所述的问题,TBB提供了两个分配器:scalable_allocatorcache_aligned_allocator,它们分别定义于tbb/scalable_allocator.h和 tbb/cache_aligned_allocator.h里。

  • scalable_allocator 解决了分配竞争的情况,它并没有完全防止假共享。不过每个线程从不同的内存池中取得内存,这也可以从一定程 序上避免假共享的发生。
  • cache_aligned_allocator 解决了分配竞争和假共享问题。由于分配的内存是缓存大小的倍数所以要花费更多的空间,尤其是分配大量小空 间时。我们应该在确定假共享已成为性能瓶颈时才使用cache_aligned_allocator。在你的程序中分别使用两种分配器来测试性能以确定最 终使用哪一个是个好主意。

在STL容器中使用分配器

scalable_allocator和cache_aligned_allocator与std::allocator是兼容的,我们可以和使用 std::allocator一样使用它们。下面的例子演示了使用cache_aligned_allocator作为std::vector的分配器。

std::vector< int, cache_aligned_allocator<int> >;

现在我们可以把前面的代码修改一下了:

void alloctask( int )
{  
    //vector构造和析构时会申请和释放空间
    std::vector<int, scalable_allocator<int> > data(100);
}

对比一下在你的电脑上效率提升了多少吧^_^

代替malloc,free,realloc和calloc

TBB为malloc,free,realloc和calloc提供了对应的可扩展版本:

#include "tbb\scalable_allocator.h"

void * scalable_malloc (size_t size);
void   scalable_free (void* ptr);
void * scalable_realloc (void* ptr, size_t size);
void * scalable_calloc (size_t nobj, size_t size);

代替new和delete

要完整地重载C++中的new和delete,我们要实现下面这四对new/delete操作:

void* operator new(std::size_t size) throw(std::bad_alloc);
void* operator new(std::size_t size, const std::nothrow_t&) throw( );
void* operator new[](std::size_t size) throw(std::bad_alloc);
void* operator new[](std::size_t size, const std::nothrow_t&) throw( );
void  operator delete(void* ptr) throw( );
void  operator delete(void* ptr, const std::nothrow_t&) throw( );
void  operator delete[](void* ptr) throw( );
void  operator delete[](void* ptr, const std::nothrow_t&) throw( );

我们可以利用前面说到的scalable_malloc()和scalable_free()来实现这些操作:

#include "tbb\scalable_allocator.h"

void* operator new (size_t size) throw (std::bad_alloc)
{
    if (size == 0) size = 1;
    if (void* ptr = scalable_malloc (size))
        return ptr;
    throw std::bad_alloc ( );
}
void* operator new[] (size_t size) throw (std::bad_alloc)
{
    return operator new (size);
}
void* operator new (size_t size, const std::nothrow_t&) throw ( )
{
    if (size == 0) size = 1;
    if (void* ptr = scalable_malloc (size))
        return ptr;
    return NULL;
}
void* operator new[] (size_t size, const std::nothrow_t&) throw ( )
{
    return operator new (size, std::nothrow);
}
void operator delete (void* ptr) throw ( )
{
    if (ptr != 0) scalable_free (ptr);
}
void operator delete[] (void* ptr) throw ( )
{
    operator delete (ptr);
}
void operator delete (void* ptr, const std::nothrow_t&) throw ( )
{
    if (ptr != 0) scalable_free (ptr);
}
void operator delete[] (void* ptr, const std::nothrow_t&) throw ( )
{
    operator delete (ptr, std::nothrow);
}
<<完>>

 

分享到:
评论

相关推荐

    C#制作串口助手源码程序有注解

    用C#做的一个简易串口助手再也不用别人的串口助手了HHHH 基本功能都有,代码也有很多注释,欢迎下载。

    精选毕设项目-王者荣耀故事站小程序带Vue后台.zip

    精选毕设项目-王者荣耀故事站小程序带Vue后台

    停车场管理系统c语言代码及注释

    停车场管理系统c语言

    精选毕设项目-智能机器人.zip

    精选毕设项目-智能机器人

    夕阳红公寓管理系统的设计与实现-springboot毕业项目,适合计算机毕-设、实训项目、大作业学习.zip

    Spring Boot是Spring框架的一个模块,它简化了基于Spring应用程序的创建和部署过程。Spring Boot提供了快速启动Spring应用程序的能力,通过自动配置、微服务支持和独立运行的特性,使得开发者能够专注于业务逻辑,而不是配置细节。Spring Boot的核心思想是约定优于配置,它通过自动配置机制,根据项目中添加的依赖自动配置Spring应用。这大大减少了配置文件的编写,提高了开发效率。Spring Boot还支持嵌入式服务器,如Tomcat、Jetty和Undertow,使得开发者无需部署WAR文件到外部服务器即可运行Spring应用。 Java是一种广泛使用的高级编程语言,由Sun Microsystems公司(现为Oracle公司的一部分)在1995年首次发布。Java以其“编写一次,到处运行”(WORA)的特性而闻名,这一特性得益于Java虚拟机(JVM)的使用,它允许Java程序在任何安装了相应JVM的平台上运行,而无需重新编译。Java语言设计之初就是为了跨平台,同时具备面向对象、并发、安全和健壮性等特点。 Java语言广泛应用于企业级应用、移动应用、桌面应用、游戏开发、云计算和物联网等领域。它的语法结构清晰,易于学习和使用,同时提供了丰富的API库,支持多种编程范式,包括面向对象、命令式、函数式和并发编程。Java的强类型系统和自动内存管理减少了程序错误和内存泄漏的风险。随着Java的不断更新和发展,它已经成为一个成熟的生态系统,拥有庞大的开发者社区和持续的技术创新。Java 8引入了Lambda表达式,进一步简化了并发编程和函数式编程的实现。Java 9及以后的版本继续在模块化、性能和安全性方面进行改进,确保Java语言能够适应不断变化的技术需求和市场趋势。 MySQL是一个关系型数据库管理系统(RDBMS),它基于结构化查询语言(SQL)来管理和存储数据。MySQL由瑞典MySQL AB公司开发,并于2008年被Sun Microsystems收购,随后在2010年,Oracle公司收购了Sun Microsystems,从而获得了MySQL的所有权。MySQL以其高性能、可靠性和易用性而闻名,它提供了多种特性来满足不同规模应用程序的需求。作为一个开源解决方案,MySQL拥有一个活跃的社区,不断为其发展和改进做出贡献。它的多线程功能允许同时处理多个查询,而其优化器则可以高效地执行复杂的查询操作。 随着互联网和Web应用的快速发展,MySQL已成为许多开发者和公司的首选数据库之一。它的可扩展性和灵活性使其能够处理从小规模应用到大规模企业级应用的各种需求。通过各种存储引擎,MySQL能够适应不同的数据存储和检索需求,从而为用户提供了高度的定制性和性能优化的可能性。

    2机5节点系统暂态稳定性仿真 Simulink仿真 1.基于MATLAB Simulink平台搭建2机5节点系统仿真模型,可以仿真单相接地 两相相间短路 两相接地短路 三相短路故障情况下 系统的暂态特

    2机5节点系统暂态稳定性仿真 Simulink仿真 1.基于MATLAB Simulink平台搭建2机5节点系统仿真模型,可以仿真单相接地 两相相间短路 两相接地短路 三相短路故障情况下 系统的暂态特性。 2.研究电力系统稳定器(power system stabilizer)PSS和静止无功补偿器(static var compensator)SVC对系统暂态稳定性的影响。

    思维导图制作-会计初级知识重难点-会计务实-流动资产

    本专刊的主要目的是帮助初学者系统化和结构化地掌握会计知识。我们采用思维导图的形式,将复杂的会计概念和流程进行有效的简化,旨在让学习者能够更清晰地理解这些内容,并增强记忆效果。通过视觉化的方式,读者不仅能够感受到会计知识的关联性,还能轻松掌握关键点,提升学习效率。无论是在学习新知识还是复习旧知识时,这种方法都能够为学习者提供极大的便利和帮助。

    用python3.6在电脑上实现用摄像头来人脸识别源程序

    用python3.6在电脑上实现用摄像头来人脸识别源程序

    精选毕设项目-知乎日报.zip

    精选毕设项目-知乎日报

    我的nvim的init.lua配置

    我的nvim的init.lua配置

    精选毕设项目-课程管理.zip

    精选毕设项目-课程管理

    SP_ADM_FIN_LONG_LOAN_AGE_DTL.sql

    SP_ADM_FIN_LONG_LOAN_AGE_DTL.sql

    基于fpga的ddr3读写控制,纯verilog实现,能实现多通道图像数据读写控制,模块接口清晰,可移植性高.

    基于fpga的ddr3读写控制,纯verilog实现,能实现多通道图像数据读写控制,模块接口清晰,可移植性高.

    校园周边美食探索及分享平台的设计与实现-springboot毕业项目,适合计算机毕-设、实训项目、大作业学习.zip

    Spring Boot是Spring框架的一个模块,它简化了基于Spring应用程序的创建和部署过程。Spring Boot提供了快速启动Spring应用程序的能力,通过自动配置、微服务支持和独立运行的特性,使得开发者能够专注于业务逻辑,而不是配置细节。Spring Boot的核心思想是约定优于配置,它通过自动配置机制,根据项目中添加的依赖自动配置Spring应用。这大大减少了配置文件的编写,提高了开发效率。Spring Boot还支持嵌入式服务器,如Tomcat、Jetty和Undertow,使得开发者无需部署WAR文件到外部服务器即可运行Spring应用。 Java是一种广泛使用的高级编程语言,由Sun Microsystems公司(现为Oracle公司的一部分)在1995年首次发布。Java以其“编写一次,到处运行”(WORA)的特性而闻名,这一特性得益于Java虚拟机(JVM)的使用,它允许Java程序在任何安装了相应JVM的平台上运行,而无需重新编译。Java语言设计之初就是为了跨平台,同时具备面向对象、并发、安全和健壮性等特点。 Java语言广泛应用于企业级应用、移动应用、桌面应用、游戏开发、云计算和物联网等领域。它的语法结构清晰,易于学习和使用,同时提供了丰富的API库,支持多种编程范式,包括面向对象、命令式、函数式和并发编程。Java的强类型系统和自动内存管理减少了程序错误和内存泄漏的风险。随着Java的不断更新和发展,它已经成为一个成熟的生态系统,拥有庞大的开发者社区和持续的技术创新。Java 8引入了Lambda表达式,进一步简化了并发编程和函数式编程的实现。Java 9及以后的版本继续在模块化、性能和安全性方面进行改进,确保Java语言能够适应不断变化的技术需求和市场趋势。 MySQL是一个关系型数据库管理系统(RDBMS),它基于结构化查询语言(SQL)来管理和存储数据。MySQL由瑞典MySQL AB公司开发,并于2008年被Sun Microsystems收购,随后在2010年,Oracle公司收购了Sun Microsystems,从而获得了MySQL的所有权。MySQL以其高性能、可靠性和易用性而闻名,它提供了多种特性来满足不同规模应用程序的需求。作为一个开源解决方案,MySQL拥有一个活跃的社区,不断为其发展和改进做出贡献。它的多线程功能允许同时处理多个查询,而其优化器则可以高效地执行复杂的查询操作。 随着互联网和Web应用的快速发展,MySQL已成为许多开发者和公司的首选数据库之一。它的可扩展性和灵活性使其能够处理从小规模应用到大规模企业级应用的各种需求。通过各种存储引擎,MySQL能够适应不同的数据存储和检索需求,从而为用户提供了高度的定制性和性能优化的可能性。

    吉林大学 2023级卓班 面向对象课程设计

    课设总结报告 一、需求分析 主要从服务端(Server)和客户端(Client)进行需求分析 1.1 Client 端 1.1.1 注册与登录功能 注册账号 新账号注册,输入账号密码(必填)以及一些可填可不填的账户信息(手机号,邮箱,地址)。注册成功则自动跳转登录界面 登录账号 用户输入正确的账号与对于的密码,即可登录进入系统 联系客服 若忘记密码,可以以匿名者的身份联系客服,让管理员修改你的密码。 1.1.2 商品检索功能 推荐商品功能 在首页轮转推荐商品,可以直接加入购物车。实现换一批功能,每次推荐一批商品在首页上。 商品的模糊搜索 通过模糊搜索(商品名或者简介中包含的关键词),找到想要找的商品。并可以点击并加入购物车。 1.1.3 购物车功能 加入购物车功能 在上述的商品检索中,都可以加入购物车 购物车多选式的结算和删除 可以查看自己的购物车。 在购物车中,可以多选并执行结算、单个商品删除操作、调整购物车中商品的数量。 1.1.4 历史记录功能 订单历史记录 可以查看历史记录。 退货 可以在此界面选择订单实现退货。 1.1.5 客服功能 实现与 Server

    XX人民法院大楼安保系统整体解决方案Word(103页).docx

    在科技与司法的交响曲中,智慧法院应运而生,成为新时代司法服务的新篇章。它不仅仅是一个概念,更是对法院传统工作模式的一次深刻变革。智慧法院通过移动信息化技术,为法院系统注入了强大的生命力,有效缓解了案多人少的矛盾,让司法服务更加高效、便捷。 立案、调解、审判,每一个阶段都融入了科技的智慧。在立案阶段,智慧法院利用区块链技术实现可信存证,确保了电子合同的合法性和安全性,让交易双方的身份真实性、交易安全性得到了有力见证。这不仅极大地缩短了立案时间,还为后续审判工作奠定了坚实的基础。在调解阶段,多元调解服务平台借助人工智能、自然语言处理等前沿技术,实现了矛盾纠纷的快速化解。无论是矛盾类型的多元化,还是化解主体的多元化,智慧法院都能提供一站式、全方位的服务,让纠纷解决更加高效、和谐。而在审判阶段,智能立案、智能送达、智能庭审、智能判决等一系列智能化手段的应用,更是让审判活动变得更加智能化、集约化。这不仅提高了审判效率,还确保了审判质量的稳步提升。 更为引人注目的是,智慧法院还构建了一套完善的执行体系。移动执行指挥云平台的建设,让执行工作变得更加精准、高效。执行指挥中心和信息管理中心的一体化应用,实现了信息的实时传输和交换,为执行工作提供了强有力的支撑。而执行指挥车的配备,更是让执行现场通讯信号得到了有力保障,应急通讯能力得到了显著提升。这一系列创新举措的实施,不仅让执行难问题得到了有效解决,还为构建诚信社会、保障金融法治化营商环境提供了有力支撑。智慧法院的出现,让司法服务更加贴近民心,让公平正义的阳光更加温暖人心。

    精选毕设项目-装修预约小程序.zip

    精选毕设项目-装修预约小程序

    精选毕设项目-乐租租房工具.zip

    精选毕设项目-乐租租房工具

    chromedriver-linux64_123.0.6298.0.zip

    chromedriver-linux64_123.0.6298.0

    林业产品推荐系统-springboot毕业项目,适合计算机毕-设、实训项目、大作业学习.zip

    Spring Boot是Spring框架的一个模块,它简化了基于Spring应用程序的创建和部署过程。Spring Boot提供了快速启动Spring应用程序的能力,通过自动配置、微服务支持和独立运行的特性,使得开发者能够专注于业务逻辑,而不是配置细节。Spring Boot的核心思想是约定优于配置,它通过自动配置机制,根据项目中添加的依赖自动配置Spring应用。这大大减少了配置文件的编写,提高了开发效率。Spring Boot还支持嵌入式服务器,如Tomcat、Jetty和Undertow,使得开发者无需部署WAR文件到外部服务器即可运行Spring应用。 Java是一种广泛使用的高级编程语言,由Sun Microsystems公司(现为Oracle公司的一部分)在1995年首次发布。Java以其“编写一次,到处运行”(WORA)的特性而闻名,这一特性得益于Java虚拟机(JVM)的使用,它允许Java程序在任何安装了相应JVM的平台上运行,而无需重新编译。Java语言设计之初就是为了跨平台,同时具备面向对象、并发、安全和健壮性等特点。 Java语言广泛应用于企业级应用、移动应用、桌面应用、游戏开发、云计算和物联网等领域。它的语法结构清晰,易于学习和使用,同时提供了丰富的API库,支持多种编程范式,包括面向对象、命令式、函数式和并发编程。Java的强类型系统和自动内存管理减少了程序错误和内存泄漏的风险。随着Java的不断更新和发展,它已经成为一个成熟的生态系统,拥有庞大的开发者社区和持续的技术创新。Java 8引入了Lambda表达式,进一步简化了并发编程和函数式编程的实现。Java 9及以后的版本继续在模块化、性能和安全性方面进行改进,确保Java语言能够适应不断变化的技术需求和市场趋势。 MySQL是一个关系型数据库管理系统(RDBMS),它基于结构化查询语言(SQL)来管理和存储数据。MySQL由瑞典MySQL AB公司开发,并于2008年被Sun Microsystems收购,随后在2010年,Oracle公司收购了Sun Microsystems,从而获得了MySQL的所有权。MySQL以其高性能、可靠性和易用性而闻名,它提供了多种特性来满足不同规模应用程序的需求。作为一个开源解决方案,MySQL拥有一个活跃的社区,不断为其发展和改进做出贡献。它的多线程功能允许同时处理多个查询,而其优化器则可以高效地执行复杂的查询操作。 随着互联网和Web应用的快速发展,MySQL已成为许多开发者和公司的首选数据库之一。它的可扩展性和灵活性使其能够处理从小规模应用到大规模企业级应用的各种需求。通过各种存储引擎,MySQL能够适应不同的数据存储和检索需求,从而为用户提供了高度的定制性和性能优化的可能性。

Global site tag (gtag.js) - Google Analytics