记一次使用ConcurrentDictionary优化程序性能的经验总结

项目情形

最近做项目发现有个业务逻辑性能效率巨慢, 实际上是扫描cosmos上面16个文件夹下面的数据, 每个folder下面大概分为100来个对应user的fodler, 然后对应user folder下面存放的是user的数据. 原逻辑是一个folder一个folder去scan, 然后将统计的数据按照 user和size存放到一个dictionary中, 最后汇总统计并且发邮件. 其中影响效率的部分有当前运行环境与cosmos的交互上, 不同的环境快慢不同. 另外一个就是code逻辑是串行的. 这样会导致效率很差. 整体运行完一遍, 在慢的环境上要48小时, 快的环境也得将近20小时. 于是我开始想优化code逻辑. 使用并行方式尝试提升运行效率.

使用ConcurrentDictionary

我之前大概了解过一些关于ConcurrentDictionary的概念, 知道它是一个线程安全的字典缓存. 可以用于并发场景. 对于我目前来说作为解决方案应该是适配的. 我准备使用一个核心ConcurrentDictionary作为数据缓存, 然后启用16个Task去扫描cosmos上面的16个root folder. 每个task将扫描得到的数据记录至ConcurrentDictionary当中. 最后当所有的task运行完毕后, 将ConcurrentDictionary中的值完善一下发出邮件. 初步做了一下, 在一个快的环境上, 运行了2个小时, 程序完成业务逻辑. 也就是说效率从原来的20小时提升至2小时, 提升了10倍. 效果还是非常显著的. 下面我来介绍一下具体的实现和深入探究一下ConcurrentDictionary的底层实现.

核心缓存只一行代码:

public static ConcurrentDictionary<string, UserCosmosInfo> aggregate = new ConcurrentDictionary<string, UserCosmosInfo>();

其中string为user的名字, 不会重复, 作为key正好. 然后UserCosmosInfo 是我封装的一个类, 类里面的属性是user后面需要用的:

public class UserCosmosInfo
    {
        public string Name { get; set; }

        public long TotalStorage { get; set; }

        public long Last1WeekAddedStorage { get; set; }

        public long Last2WeekAddedStorage { get; set; }

        public long Last1MonthAddedStorage { get; set; }

        public long Last6MonthsAddedStorage { get; set; }

        public long OtherMoreThan6Months { get; set; }

        public List<CosmosStreamInfo> TopBigFiles { get; set; }
    }

启动16个Task分别扫描数据:

public static void AggregateCosmosFolder(string baseFolder, string[] filter, bool recursive = true)
        {
            var folderCount = 16;
            var tasks = new Task[folderCount];
            for (int i = 0; i < folderCount; i++)
            {
                int param = i;
                tasks[i] = Task.Run(() => AggregateCosmosFolderInTask(param, baseFolder, filter, true));
            }
            Task.WaitAll(tasks);
        }

实际每个task运行的业务类(简略版):

private static void AggregateCosmosFolderInTask(int currentFolder, string baseFolder, string[] filter, bool recursive = true)
        {
            var threadId = Thread.CurrentThread.ManagedThreadId;
            for (int j = 0; j < filter.Length; j++)
            {
                string u = filter[j];
                string prefix = $"{baseFolder}/_{currentFolder.ToString("x")}/{u}/";
                if (CosmosDirExist(prefix))
                {
                    IEnumerable<StreamInfo> streams = GetFiles(prefix, recursive);
                    if (streams != null)
                    {
                        foreach (StreamInfo s in streams)
                            {
                                //调用 tryAdd, 将key u 和 value 加入缓存, 此处 tryAdd的底层实现了并发控制, 稍后我们看看底层的代码实现....
                                Program.aggregate.TryAdd(u, new UserCosmosInfo()
                                {
                                    ///省略代码篇幅
                                });

                                // new 一个新的 value, 这个value是需要更新到缓存中的.
                                var newValue = new UserCosmosInfo()
                                {
                                    ///省略代码篇幅
                                };

                                // AddOrUpdate 方法用于更新缓存的数据, 底层同样是并发控制做的很到位了, 所以我们直接放心使用......
                                // 注意它的参数, key 传 u, 将上面的 newValue 传进去, 在写一个 Fun<> 委托, 委托在执行时也是线程安全的, 至于实现方式也需要看底层源码.
                                Program.aggregate.AddOrUpdate(u, newValue, (key, existingValue) =>
                                {
                                    existingValue.TotalStorage += newValue.TotalStorage;
                                    return existingValue;
                                });
                            }
                    }
                }
            }
        }

新的业务类的实现大致是这样得. 主要是使用了 ConcurrentDictionary 的一些API, 下一步我将探究底层的实现

今天没时间了, 下次继续写.

参考链接

https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/how-to-add-and-remove-items

https://stackoverflow.com/questions/30225476/task-run-with-parameters

原文地址:https://www.cnblogs.com/it-dennis/p/11990914.html

时间: 2024-08-30 13:46:31

记一次使用ConcurrentDictionary优化程序性能的经验总结的相关文章

浅谈优化程序性能(下)

前言 在上一篇随笔中,我们谈到最小化一个计算中的操作数量不一定会提高它的性能.现在,就让我们来解开为什么会出现这种情况的原因吧. 处理器体系结构 在计算机的处理器中,处理一条指令包括很多操作,可以分为取指(fetch).译码(decode).执行(execute).访存(memory).写回(write back)和更新程序计数器(PC update)等几个阶段.这些阶段可以在流水线上同时进行,如下图所示: 上图中,F.D.E.M 和 W 分别代表上述五个阶段.当然,现代的处理器比这个示例要复杂

深入理解计算机系统(5.1)------优化程序性能

你能获得的对程序最大的加速比就是当你第一次让它工作起来的时候. 在讲解如何优化程序性能之前,我们首先要明确写程序最主要的目标就是使它在所有可能的情况下都能正常工作,一个运行的很快的程序但是却是错误的结果是没有任何用处的,所以我们在进行程序性能优化之前,首先要保证程序能正常运行,且结果是我们需要的. 而且在很多情况下,让程序跑的更快是我们必须要解决的问题.比如一个程序要实时处理视频帧或者网络包,那么一个运行的很慢的程序就不能解决此问题.再比如一个计算任务计算量非常大,需要数日或者数周,如果我们哪怕

《深入理解计算机系统》 优化程序性能的几个方法

本文几个优化程序性能的方法出自CSAPP第五章,通过不断修改源代码,试图欺骗编译器产生有效的代码 我们先引入度量标准每元素的周期数(CPE),表示程序性能. 我们先定义一个数据结构   data_t 代表数据类型 1 typedef struct{ 2 long len; 3 data_t *data; 4 }vec_rec,*vec_prt; 以及常数IDENT和OP以便在后续的代码中进行不同的操作 //对所有向量的元素求和 #define IDENT 0 #define OP + //对所有

优化程序性能(CSAPP:5)

[前言]虽然现在没有接触过大型项目,但是工作了会注重性能.学习一下,应该能更好更快的理解别人写的经典优秀的代码.结合CSAPP和自己的理解,总结一下. 一.程序优化综述 1.高效程序的特点 (1)适当的算法和数据结构.方法和数据的组织形式无疑是最关键的,是优化的基础: (2)代码能够被编译器转化成高效的可执行代码.需要深入了解使用的编译器的优化方法,和常见的优化策略: (3)运用现代并行编程技术.多核以及硬件支持提供更大的加速可能,例如GPU: 2.优化程序的一般步骤 (1)消除不必要的工作,例

浅谈优化程序性能(上)

前言 我们知道,多项式定义为: 在几何学中,多项式是最简单的平滑曲线.简单是指它仅由乘法及加法构成,平滑是因为它类同口语中的平滑,以数学术语来说,它是无限可微,即它的所有高次微分都存在.事实上,多项式的微分也是多项式.简单及平滑的特点,使多项式在数值分析.图论,以及电脑绘图等,都发挥极大的作用.多项式求值是解决许多问题的核心技术.以数值分析为例,多项式函数常常用作对数学库中的三角函数求近似值. 现在,让我们来用 C 语言写一个对多项式求值的函数吧. 直接的算法 直接按照多项式的定义使用循环求值:

优化程序性能(3)——提高并行性

在之前的学习中,程序的性能是受运算单元的延迟限制的.正如我们表明的,执行加法和乘法的功能单元是完全流水线化的,这意味着它们可以每个时钟周期开始一个新操作,并且有些操作可以被多个功能单元执行.硬件具有以更高速率执行乘法和加法的潜力,但是代码不能利用这种能力,即使是使用循环展开也不能,这是因为我们将积累值放在一个单独的变量acc中,在前面的计算完成之前,都不能计算acc的新值(顺序依赖).虽然计算acc值的功能单元能够每个时钟周期开始一个新操作,但是它只会每L(L是合并操作的延迟)个周期开始一条新操

web应用程序性能优化

web应用程序基本上都是在浏览器地址栏输入一段网站,然后进入,最后浏览器显示你想要的东西. 这就是用户所能体会到的东西.那作为程序员我们看到了什么呢? 一次HTTP 请求主要的流程是: 1.DNS服务器解析域名(浏览器地址栏的地址)获取相应的IP地址.端口号. 服务名. 2.客户端根据解析后的地址向服务啊发送请求(建立与服务器的联接). 3.服务器根据用户的请求信息处理请求,并做出响应. 4.浏览器更具服务器响应的数据(HTML/css/js)渲染页面. 那要优化程序性能,作为程序员我们能优化哪

iOS 程序性能优化

前言 转载自:http://www.samirchen.com/ios-performance-optimization/ 程序性能优化不应该是一件放在功能完成之后的事,对性能的概念应该从我们一开始写代码时就萦绕在我们脑子里.了解 iOS 程序性能优化的相关知识点,从一开始就把它们落实到代码中是一种好的习惯. 初级技巧 使用复用机制 在我们使用 UITableView 和 UICollectionView 时我们通常会遇到「复用 Cell」这个提法,所谓「复用 Cell」就是指当需要展示的数据条

.NET程序性能优化基本要领

想了解更多关于新的编译器的信息,可以访问     .NET Compiler Platform ("Roslyn") 基本要领 在对.NET 进行性能调优以及开发具有良好响应性的应用程序的时候,请考虑以下这些基本要领: 要领一:不要过早优化 编写代码比想象中的要复杂的多,代码需要维护,调试及优化性能. 一个有经验的程序员,通常会对自然而然的提出解决问题的方法并编写高效的代码. 但是有时候也可能会陷入过早优化代码的问题中.比如,有时候使用一个简单的数组就够了,非要优化成使用哈希表,有时候