精华内容
下载资源
问答
  • 局部性原理——各类优化的基石

    千次阅读 2019-07-28 16:38:21
    学过计算机底层原理、了解过很多架构设计或者是做过优化的同学,应该很熟悉局部性原理。即便是非计算机行业的人,在做各种调优、提效时也不得不考虑到局部性,只不过他们不常用局部性一词。如果抽象程度再高一些,...

    学过计算机底层原理、了解过很多架构设计或者是做过优化的同学,应该很熟悉局部性原理。即便是非计算机行业的人,在做各种调优、提效时也不得不考虑到局部性,只不过他们不常用局部性一词。如果抽象程度再高一些,甚至可以说地球、生命、万事万物都是局部性的产物,因为这些都是宇宙中熵分布布局、局部的熵低导致的,如果宇宙中处处熵一致,有的只有一篇混沌。

    所以什么是 局部性 ?这是一个常用的计算机术语,是指处理器在访问某些数据时短时间内存在重复访问,某些数据或者位置访问的概率极大,大多数时间只访问_局部_的数据。基于局部性原理,计算机处理器在设计时做了各种优化,比如现代CPU的多级Cache、分支预测…… 有良好局部性的程序比局部性差的程序运行得更快。虽然局部性一词源于计算机设计,但在当今分布式系统、互联网技术里也不乏局部性,比如像用redis这种memcache来减轻后端的压力,CDN做素材分发减少带宽占用率……

    局部性的本质是什么?其实就是概率的不均等,这个宇宙中,很多东西都不是平均分布的,平均分布是概率论中几何分布的一种特殊形式,非常简单,但世界就是没这么简单。我们更长听到的发布叫做高斯发布,同时也被称为正态分布,因为它就是正常状态下的概率发布,起概率图如下,但这个也不是今天要说的。
    在这里插入图片描述
    其实有很多情况,很多事物有很强的头部集中现象,可以用概率论中的泊松分布来刻画,这就是局部性在概率学中的刻画形式。
    在这里插入图片描述
    在这里插入图片描述
    上面分别是泊松分布的示意图和概率计算公式,λ\lambda 表示单位时间(或单位面积)内随机事件的平均发生次数,ee表示自然常数2.71828…,k表示事件发生的次数。要注意在刻画局部性时λ\lambda表示不命中高频数据的频度,λ\lambda越小,头部集中现象越明显。

    局部性分类

    局部性有两种基本的分类, 时间局部性空间局部性 ,按Wikipedia的资料,可以分为以下五类,其实有些就是时间局部性和空间局部性的特殊情况。

    时间局部性(Temporal locality):

    如果某个信息这次被访问,那它有可能在不久的未来被多次访问。时间局部性是空间局部性访问地址一样时的一种特殊情况。这种情况下,可以把常用的数据加cache来优化访存。

    空间局部性(Spatial locality):

    如果某个位置的信息被访问,那和它相邻的信息也很有可能被访问到。 这个也很好理解,我们大部分情况下代码都是顺序执行,数据也是顺序访问的。

    内存局部性(Memory locality):

    访问内存时,大概率会访问连续的块,而不是单一的内存地址,其实就是空间局部性在内存上的体现。目前计算机设计中,都是以块/页为单位管理调度存储,其实就是在利用空间局部性来优化性能。

    分支局部性(Branch locality)

    这个又被称为顺序局部性,计算机中大部分指令是顺序执行,顺序执行和非顺序执行的比例大致是5:1,即便有if这种选择分支,其实大多数情况下某个分支都是被大概率选中的,于是就有了CPU的分支预测优化。

    等距局部性(Equidistant locality)

    等距局部性是指如果某个位置被访问,那和它相邻等距离的连续地址极有可能会被访问到,它位于空间局部性和分支局部性之间。 举个例子,比如多个相同格式的数据数组,你只取其中每个数据的一部分字段,那么他们可能在内存中地址距离是等距的,这个可以通过简单的线性预测就预测是未来访问的位置。

    实际应用

    计算机领域关于局部性非常多的利用,有很多你每天都会用到,但可能并没有察觉,另外一些可能离你会稍微远一些,接下来我们举几个例子来深入了解下局部性的应用。

    计算机存储层级结构

    极客时间
    上图来自极客时间徐文浩的《深入浅出计算机组成原理》,我们以目前常见的普通家用电脑为例 ,分别说下上图各级存储的大小和访问速度,数据来源于https://people.eecs.berkeley.edu/~rcs/research/interactive_latency.html
    在这里插入图片描述
    从最快的L1 Cache到最慢的HDD,其两者的访存时间差距达到了6个数量级,即便是和内存比较,也有几百倍的差距。举个例子,如果CPU在运算是直接从内存中读取指令和数据,执行一条指令0.3ns,然后从内存读下一条指令,等120ns,这样CPU 99%计算时间都会被浪费掉。但就是因为有局部性的存在,每一层都只有少部分数据会被频繁访问,我们可以把这部分数据从底层存储挪到高层存储,可以降低大部分的数据读取时间。
      
    可能有些人好奇,为什么不把L1 缓存做的大点,像内存那么大,直接替代掉内存,不是性能更好吗?虽然是这样,但是L1 Cache单位价格要比内存单位的价格贵好多(大概差200倍),有兴趣可以了解下DRAM和SRAM。

    我们可以通过编写高速缓存友好的代码逻辑来提升我们的代码性能,有两个基本方法 。

    1. 让最常见的情况运行的快,程序大部分的运行实际都花在少了核心函数上,而这些函数把大部分时间都花在少量循环上,把注意力放在这些代码上。
    2. 让每个循环内缓存不命中率最小。比如尽量不要列遍历二维数组。

    MemCache

    在这里插入图片描述
    MemCache在大型网站架构中经常看到。DB一般公司都会用mysql,即便是做了分库分表,数据数据库单机的压力还是非常大的,这时候因为局部性的存在,可能很多数据会被频繁访问,这些数据就可以被cache到像redis这种memcache中,当redis查不到数据,再去查db,并写入redis。
      
    因为redis的水平扩展能力和简单查询能力要比mysql强多了,查起来也快。所以这种架构设计有几个好处:

    1. 加快了数据查询的平均速度。
    2. 大幅度减少DB的压力。

    CDN

    CDN的全称是Content Delivery Network,即内容分发网络(图片来自百度百科) 。CDN常用于大的素材下发,比如图片和视频,你在淘宝上打开一个图片,这个图片其实会就近从CDN机房拉去数据,而不是到阿里的机房拉数据,可以减少阿里机房的出口带宽占用,也可以减少用户加载素材的等待时间。
    在这里插入图片描述
    CDN在互联网中被大规模使用,像视频、直播网站,电商网站,甚至是12306都在使用,这种设计对公司可以节省带宽成本,对用户可以减少素材加载时间,提升用户体验。看到这,有没有发现,CDN的逻辑和Memcache的使用很类似,你可以直接当他是一个互联网版的cache优化。

    Java JIT

    JIT全称是Just-in-time Compiler,中文名为即时编译器,是一种Java运行时的优化。Java的运行方式和C++不太一样,因为为了实现write once, run anywhere的跨平台需求,Java实现了一套字节码机制,所有的平台都可以执行同样的字节码,执行时有该平台的JVM将字节码实时翻译成该平台的机器码再执行。问题在于字节码每次执行都要翻译一次,会很耗时。
      在这里插入图片描述
    图片来自郑雨迪Introduction to Graal ,Java 7引入了tiered compilation的概念,综合了C1的高启动性能及C2的高峰值性能。这两个JIT compiler以及interpreter将HotSpot的执行方式划分为五个级别:

    • level 0:interpreter解释执行
    • level 1:C1编译,无profiling
    • level 2:C1编译,仅方法及循环back-edge执行次数的profiling
    • level 3:C1编译,除level 2中的profiling外还包括branch(针对分支跳转字节码)及receiver type(针对成员方法调用或类检测,如checkcast,instnaceof,aastore字节码)的profiling
    • level 4:C2编译

    通常情况下,一个方法先被解释执行(level 0),然后被C1编译(level 3),再然后被得到profile数据的C2编译(level 4)。如果编译对象非常简单,虚拟机认为通过C1编译或通过C2编译并无区别,便会直接由C1编译且不插入profiling代码(level 1)。在C1忙碌的情况下,interpreter会触发profiling,而后方法会直接被C2编译;在C2忙碌的情况下,方法则会先由C1编译并保持较少的profiling(level 2),以获取较高的执行效率(与3级相比高30%)。

    这里将少部分字节码实时编译成机器码的方式,可以提升java的运行效率。可能有人会问,为什么不预先将所有的字节码编译成机器码,执行的时候不是更快更省事吗?首先机器码是和平台强相关的,linux和unix就可能有很大的不同,何况是windows,预编译会让java失去夸平台这种优势。 其次,即时编译可以让jvm拿到更多的运行时数据,根据这些数据可以对字节码做更深层次的优化,这些是C++这种预编译语言做不到的,所以有时候你写出的java代码执行效率会比C++的高。

    CopyOnWrite

    CopyOnWrite写时复制,最早应该是源自linux系统,linux中在调用fork() 生成子进程时,子进程应该拥有和父进程一样的指令和数据,可能子进程会修改一些数据,为了避免污染父进程的数据,所以要给子进程单独拷贝一份。出于效率考虑,fork时并不会直接复制,而是等到子进程的各段数据需要写入才会复制一份给子进程,故此得名 写时复制
      
    在计算机的世界里,读写的分布也是有很大的局部性的,大多数情况下读远大于写, 写时复制 的方式,可以减少大量不必要的复制,提升性能。 另外这种方式也不仅仅是用在linux内核中,java的concurrent包中也提供了CopyOnWriteArrayList CopyOnWriteArraySet。像Spark中的RDD也是用CopyOnWrite来减少不必要的RDD生成。

    处理

    上面列举了那么多局部性的应用,其实还有很多很多,我只是列举出了几个我所熟知的应用,虽然上面这些例子,我们都利用局部性得到了能效、成本上的提升。但有些时候它也会给我们带来一些不好的体验,更多的时候它其实就是一把双刃剑,我们如何识别局部性,利用它好的一面,避免它坏的一面?

    识别

    文章开头也说过,局部性其实就是一种概率的不均等性,所以只要概率不均等就一定存在局部性,因为很多时候这种概率不均太明显了,非常好识别出来,然后我们对大头做相应的优化就行了。但可能有些时候这种概率不均需要做很详细的计算才能发现,最后还得核对成本才能考虑是否值得去做,这种需要具体问题具体分析了。

    如何识别局部性,很简单,看概率分布曲线,只要不是一条水平的直线,就一定存在局部性。

    利用

    发现局部性之后对我们而言是如何利用好这些局部性,用得好提升性能、节约资源,用不好局部性就会变成阻碍。而且不光是在计算机领域,局部性在非计算机领域也可以利用。

    性能优化

    上面列举到的很多应用其实就是通过局部性做一些优化,虽然这些都是别人已经做好的,但是我们也可以参考其设计思路。

    恰巧最近我也在做我们一个java服务的性能优化,利用jstack、jmap这些java自带的分析工具,找出其中最吃cpu的线程,找出最占内存的对象。我发现有个redis数据查询有问题,因为每次需要将一个大字符串解析很多个键值对,中间会产生上千个临时字符串,还需要将字符串parse成long和double。redis数据太多,不可能完全放的内存里,但是这里的key有明显的局部性,大量的查询只会集中在头部的一些key上,我用一个LRU Cache缓存头部数据的解析结果,就可以减少大量的查redis+解析字符串的过程了。

    另外也发现有个代码逻辑,每次请求会被重复执行几千次,耗费大量cpu,这种热点代码,简单几行改动减少了不必要的调用,最终减少了近50%的CPU使用。

    非计算机领域

    《高能人士的七个习惯》里提到了一种工作方式,将任务划分为重要紧急、不重要但紧急、重要但不紧急、不重要不紧急四种,这种划分方式其实就是按单位时间的重要度排序的,按单位时间的重要度越高收益越大。《The Effective Engineer》里直接用leverage(杠杆率)来衡量每个任务的重要性。这两种方法差不多是类似的,都是优先做高收益率的事情,可以明显提升你的工作效率。

    这就是工作中收益率的局部性导致的,只要少数事情有比较大的收益,才值得去做。还有一个很著名的法则__82法则__,在很多行业、很多领域都可以套用,80%的xxx来源于20%的xxx ,80%的工作收益来源于20%的工作任务,局部性给我们的启示“永远关注最重要的20%” 。

    避免

    上面我们一直在讲如何通过局部性来提升性能,但有时候我们需要避免局部性的产生。 比如在大数据运算时,时常会遇到数据倾斜、数据热点的问题,这就是数据分布的局部性导致的,数据倾斜往往会导致我们的数据计算任务耗时非常长,数据热点会导致某些单节点成为整个集群的性能瓶颈,但大部分节点却很闲,这些都是我们需要极力避免的。

    一般我们解决热点和数据切斜的方式都是提供过重新hash打乱整个数据让数据达到均匀分布,当然有些业务逻辑可能不会让你随意打乱数据,这时候就得具体问题具体分析了。感觉在大数据领域,局部性极力避免,当然如果没法避免你就得通过其他方式来解决了,比如HDFS中小文件单节点读的热点,可以通过减少加副本缓解。其本质上没有避免局部性,只增加资源缓解热点了,据说微博为应对明星出轨Redis集群也是采取这种加资源的方式。

    参考资料

    1. 维基百科局部性原理
    2. 《计算机组成与设计》 David A.Patterson / John L.Hennessy
    3. 《深入浅出计算机组成原理》 极客时间 徐文浩
    4. 《深入理解计算机系统》 Randal E.Bryant / David O’Hallaron 龚奕利 / 雷迎春(译)
    5. interactive latencies
    6. Introduction to Graal 郑雨迪
    展开全文
  • 善用性能工具进行SQL整体优化

    千次阅读 2017-07-06 22:30:01
    今天我们首先学习关于数据库整体优化都有哪些性能工具,接着分析这些工具的特点,并结合案例进行探索,最后再进行总结和思考。 总体学习思路如下图所示: 都有哪些性能工具 这里首先要分成两部分:一种...

    SQL优化是一个复杂的工程,首先要讲究从整体到局部。今天我们首先学习关于数据库整体优化都有哪些性能工具,接着分析这些工具的特点,并结合案例进行探索,最后再进行总结和思考。

    总体学习思路如下图所示:

    善用性能工具进行SQL整体优化

    都有哪些性能工具

    这里首先要分成两部分:一种是不同调优场景的分析,可分为单纯场景的优化和复杂场景的优化;而另一种是基于这些场景的工具应用,就是针对单纯场景的优化手段和复杂场景的优化手段。

    善用性能工具进行SQL整体优化

    1、不同调优场景分析

    我们继续探讨,单纯是有多单纯呢?哦,其实可以理解为无菌真空实验室里的实验。比如一条SQL很慢,原因是未走高效的索引查询而走全表扫描,加个索引就快了,执行速度从10s变成了0.1s;或者一条SQL执行速度被优化到1s左右,逻辑读控制在50个左右,应该就已经OK。这就是单纯的环境,我们差不多无须再考虑优化了。

    那啥是复杂呢?那就是,刚才那个语句加了索引后,本应该从10s变成0.1s,结果还是10s,甚至变成30s了,这是咋回事呢?原来,现在系统是整体出问题了,数据库主机资源耗尽,啥语句都跑不快的。

    还有那个逻辑读在50左右的SQL,如果一天执行几百几千万次,这要是能将逻辑读降低一点,得省多少的逻辑读啊。原来复杂环境真的很复杂,要考虑SQL本身没问题而是被环境影响,还要考虑SQL的执行频率,判断其调优价值与调优空间,这些在单纯的环境里,是不用考虑的。

    善用性能工具进行SQL整体优化

    2、不同场景对应工具

    接下来,我们说说这两种场景对应的工具的使用。关于局部分析调优工具,这个其实就是在说SQL的执行计划了,这是SQL优化最重要的手段之一,通过分析执行计划,我们可以知道SQL语句的访问路径,知道它慢在哪里,从而进行SQL优化。由于在随后的章节中我们会详细介绍执行计划相关知识,这里就不再细述了。

    关于整体的调优工具,这里我们先撇开主机、网络、存储等层面的因素,暂时从数据库的整体层面入手。主要工具有AWR、ASH、ADDM、AWRDD这四个工具。其中AWR是关注数据库的整体性能的报告;ASH是数据库中的等待事件与哪些SQL具体对应的报告;ADDM是Oracle给出的一些建议;而AWRDD是Oracle针对不同时段的性能的一个比对报告,比如今天早上9点系统很慢,而昨天这个时候很正常,很多人就想知道今天早上9点和昨天早上9点有什么不同,于是就有了这个报告。

    善用性能工具进行SQL整体优化

    整体分析调优是必需的,那么我们对此的学习也有规律可循。首先是获取系统整体信息的手段,一般通过报告和日志获取。好比破案一样,这就是收集证据的阶段。接下来要找到蛛丝马迹,那就是如何发现问题。在本书中就是需要关注提取到的这些报告的哪些要点、哪些关键字,具体流程图如下:

    善用性能工具进行SQL整体优化

    整体性能工具的要点

    现代人对健康都比较重视,每年都会进行健康体检。其实数据库性能工具的应用(报告获取和关注要点)和体检是非常类似的。

    1. 报告的获取

    Oracle性能报告分成AWR、ASH、ADDM、AWRDD和AWRSQRPT这5个类型。

    什么?这么多,好复杂啊,记也记不住,我不想听不想听!

    别急,你只要去医院体检过,你就能听懂。

    Really?

    我们去医院体检,最终会得到一份体检报告,往往能看到很多总体性指标,这些指标会判断你是否健康。没毛病最好,万一有毛病,报告里要进一步判断是什么毛病,是高血压,还是骨质增生,还是胃有毛病……这就是现实中的体检报告。

    而Oracle提供的一种性能收集和分析工具,它能提供一个时间段内整个系统资源使用情况的报告,这个报告里有很多总体性指标来判断系统是否健康。没毛病最好,万一有毛病,问题出在什么模块,是日志切换过于频繁,还是硬解析过大,还是某些SQL相关等待事件在耗资源……这就是AWR报告。这样看来,体检报告和AWR报告非常类似。

    假设体检报告说你有胃病,很可能只告诉你胃有问题,却无法告诉你具体啥毛病,因为你手上的体检报告不会详细到拥有你胃部所有相关指标。你要得到这些指标需要做进一步信息收集,那就是胃镜。同样假设你的数据库是SQL相关等待事件问题,AWR报告很可能只告诉你有这个问题而无法告诉你是哪些SQL引发的。你要得到这些指标,想了解具体某些SQL和相关等待事件的对应需要做进一步的信息收集,那就是ASH报告。看来对比胃镜和ASH报告,二者也非常类似。

    刚才说的胃病,或许是医生告诉你的,因为上面有很多指标你无法读懂,这时如果你能拿到一张医生的病历卡记录,这里没有指标,只有白底黑字用文字描述的病情,告诉你要如何治疗,那你一定会看得很明白。同样假设,如果将含各种晦涩的指标的数据库体检报告用一些白底黑字的文字代替,用文字直接说明数据库遇到了什么问题,告诉你该如何去优化,那新手一定会看得很明白,这就是ADDM报告。看来病历卡记录和ADDM报告,二者也非常类似。

    假如你在一年前也做过体检,并将报告带到了医院,负责任的医生就一定会让你将旧的体检报告也提供给他。他会认真地比对两张报告,查看他关注的健康指标是否有异常波动,这些波动对医生很有参考意义,往往预示着病情的发展趋势。好了,别紧张,这只是比喻。

    假设你有系统新旧两个时段的两份AWR报告,负责任的DBA一定会让你将旧的AWR报告也提供给他。他会认真地比对两份报告,查看他关注的数据库指标是否有异常波动,这些波动对DBA很有参考意义,往往预示着数据库性能瓶颈的发展趋势。Oracle提供了一个工具能够将两个时段的AWR报告合并,并能方便地显示出比对信息,这个工具就是AWRDD。看来医生分析前后两次体检报告的动作和AWRDD报告比起来,两者也非常类似。

    大家知道做胃镜是一件很麻烦的事(类似ASH报告),如果没毛病就没必要让我们遭这罪。可万一体检报告无情地告诉你胃有毛病,甚至是医生分析你前后两次体检报告(类似ADDM)后告诉你胃病在加速中,你被迫无奈只好去做胃镜了。做完后医生发现你胃部有大量息肉,却无法判断这些息肉是否为良性。于是还要做进一步的检查,这就是活检。不要紧张,平时注意健康生活就好。同样ASH报告判断出某些SQL有问题,却无法得到执行计划等更详细的信息,只能依靠AWRSQRPT去获取这些信息。看来活检和AWRSQRPT报告比起来,两者也非常类似。

    最后恭喜你,活检报告显示未产生癌变,只要好好治疗,注意身体,胃就能恢复健康!看本书的读者们,你们都是IT人士,生活无规律加班熬夜者居多,一定要注意身体哦!

    对了,还有一件最重要的事没交代。大家似乎搞懂了Oracle五大性能报告,可是这些好东西在哪里才能得到呢?别着急,后续章节马上就会告诉你如何获取这五大性能报告。

    2. 报告的关注点

    如果患者拿着有各种晦涩指标的体检报告来到门诊请教医生,他一定会关注各种指标来判断患者具体是什么毛病。同样你也会对Oracle的性能报告中的各种指标进行关注来判断数据库出了什么毛病。两者非常类似,关注不同的指标,都是为了施救,前者救人,后者救数据库。

    听起来是不是很激动,恨不得马上就要开始当救库英雄了!别急,接下来还要告诉你关注什么,然后在案例中让你感受一下什么叫救库英雄。

    特别提醒:

    这里有一个特别值得注意的地方,那就是性能报告的采样时间。Oracle默认是每小时产生一个采样点,你可以收集每个小时的性能报告。我们对此要敏感,比如你的性能故障是发生在今天早上7点~8点。然后系统自动恢复了,你获取一张8点~9点的性能报告来查问题,就毫无意义了。

    3、五大性能报告的获取1AWR的获取与说明

    获取AWR报告的方式有两种:一种是直接获取方式,调后台脚本awrrpt.sql来获取,执行方式一般是在sqlplus下执行@?/rdbms/admin/awrrpt.sql;

    另一种则是通过调用命令包,获取dbms_workload_repository这个包的awr_report_html程序,用SQL命令的形式输出内容。

    Select output from table(dbms_workload_repository.awr_report_html

    (v_dbid, v_instance_number,v_min_snap_id,v_max_snap_id))

    (1)直接获取

    试验1(未使用批量提交):

    善用性能工具进行SQL整体优化

    接下来通过提示就可以生成AWR报告了,具体步骤略去,详情请扫本章最后的二维码。

    试验2(单机下,正确使用批量提交):

    善用性能工具进行SQL整体优化

    接下来通过提示就可以生成awr报告了,具体步骤略去。

    (2)通过调用命令包获取

    直接调用工具包的方式,特别适合用在程序自动获取报告的场景。

    善用性能工具进行SQL整体优化

    注:其中977587123是数据库的主机标识,可以在数据库的数据字典中查到,1是标识实例,如果是RAC,就有1和2两个,单机就只有1。1920和1921是两个断点时间,比如9点和10点之间。

    2ASH的获取与说明

    获取ASH报告的方式也有两种:一种是直接获取方式,调后台脚本ashrpt.sql来获取,执行方式一般是在sqlplus下执行@?/rdbms/admin/ashrpt.sql;另一种则是通过调用命令包,获取dbms_workload_repository这个包的ash_report_html程序。用SQL命令的形式输出内容。

    select output from table(dbms_workload_

    repository.ash_report_html( dbid,inst_num,l_btime,l_etime)

    (1)直接获取

    善用性能工具进行SQL整体优化

    说明:

    1. 如果你是一路回车,就是获取最近5分钟的ASH报告。

    2. 如果你根据Oldest ASH sample available 时间,然后回车,选择的是目前可收集的最长ASH运行情况。

    3. 你可以选择Oldest ASH sample available和Latest ASH sample available之间时间,然后输入时长,比如30表示30分钟,取你要取的任何时段的ASH报告。

    4. ASH报告的获取不同于AWR的地方在于,快照之间有无重启动作不影响报告的获取。

    5. ASH报告可以直接手工获取,比如select output from table(dbms_workload_ repository.ash_report_html( dbid,inst_num,l_btime,l_etime)。

    (2)通过调用命令包获取

    直接调用工具包的方式,特别适合用在程序自动获取报告的场景。

    善用性能工具进行SQL整体优化

    注:其中977587123是数据库的主机标识,可以在数据库的数据字典中查到,1是标识实例,如果是RAC,就有1和2两个,单机就只有1。SYSDATE-30/1440,SYSDATE-1/1440 分别是开始时间和结束时间。

    3ADDM的获取与说明

    获取ADDM报告的方式也有两种,一种是直接获取方式:调后台脚本addmrpt.sql来获取,执行方式一般是在sqlplus下执行@?/rdbms/admin/addmrpt.sql。另一种则是通过调用命令包的方式获取:调用dbms_workload_repository这个包的addm_report_html程序。用SQL命令的形式输出内容。

    - Create an ADDM task.

    DBMS_ADVISOR.create_task (

    advisor_name => 'ADDM',

    task_name => 'MYADDM',

    task_desc => 'MYADDM');

    (1)直接获取

    @?/rdbms/admin/addmrpt.sql

    具体执行过程略去。

    (2)通过调用命令包获取

    注:直接调用工具包的方式,适合用在自动获取报告的场景。

    善用性能工具进行SQL整体优化

    4AWRDD的获取与说明

    获取AWRDD报告一般是用直接获取的方式,这个脚本的交互部分需要输入要进行对比的两个awr报告的begin snap_id与end snap_id,然后输入对比结果报告的名称,这里就不详细介绍了,请读者自行试验完成。

    直接获取:

    @?/rdbms/admin/awrddrpt.sql

    具体略去。

    5AWRSQ获取与说明

    获取AWRSQRPT报告的关键之处在于,交互部分要输入所要分析的SQL的SQL_ID,这是关键之处。而这个SQL_ID可以从AWR报告中获取。

    善用性能工具进行SQL整体优化

    以上5个报告的获取本身并不难,操作一遍就会了,笔者也会再提供在线操作视频,让大家实际体会一遍。现在关键在于,要明白这5个报告的作用和相互之间的区别,搞懂这些,调优之路就算完成过半了。当然,接下来如何分析读懂这五大报告的关键指标就非常重要了,有一些指标你必须关注,否则你就当不了“医生”了。

    4、五大报告关注的要点1AWR的关注点

    AWR报告是五大报告中最全面最重要的一个报告,它的相关指标也显得格外重要。这里我们列出DB Time、load_profile、efficiency percentages、top 5 events、SQL Statistics、Segment_statistics这6个指标入手分析。

    (1)AWR关注点1之DB Time

    DB Time这个指标主要用来判断当前系统有没有遇到相关瓶颈,是否较为繁忙导致等待时长很长。一般来说,Elapsed时间乘以CPU个数的时间如果结果大于DB Time,我们认为系统压力不大,反之则压力较大。如下例子中,60.11×64=3847.04<5990.6,说明系统现在还是比较繁忙的。

    善用性能工具进行SQL整体优化

    (2)AWR关注点2之load_profile

    load_profile这个指标主要用来展现当前系统的一些指示性能的总体参数,比如经典的Redo size就是用来显示平均每秒的日志尺寸和平均每个事务的日志尺寸,结合Transactions这个每秒事务数的指标,就可以分析出当前事务的繁忙程度。

    下图中显示每秒有6777.1个事务数,这在现实中几乎不可能,现实中的运营商系统一般在200上下比较正常,超过1000就属于非常繁忙了。

    善用性能工具进行SQL整体优化

    把上图和下面的图进行比较,就非常明显了,下图显示每秒有0.6个事务,平均每个事务产生的日志尺寸是7位数。这说明系统是一个提交不频繁的处理大任务事件的系统。而上图的尺寸是3位数。这里非常容易看出,这是一个提交非常频繁且每个事务都非常小的密集提交系统。

    善用性能工具进行SQL整体优化

    (3)AWR关注点3之efficiency percentages

    efficiency percentages是一些命中率指标,其中Buffer Hit、Library Hit等都表示SGA(System global area)的命中率。在下图中Soft Parse指标表示共享池的软解析率,在OLTP系统中如果该指标低于90%应当引起你的注意,这表示存在未使用绑定变量的情况。我们通过比对两个报告,可以看出明显差异,如下面系列图所示。

    报告1(未有效地使用绑定变量,产生大量硬解析的场景)。

    善用性能工具进行SQL整体优化

    报告2(有效地使用绑定变量,进行绑定变量优化后的场景)。

    善用性能工具进行SQL整体优化

    (4)AWR关注点4之top 5 events

    等待事件是衡量数据库整体优化情况的重要指标,通过观察Top 5 Timed Foreground Events模块的Event和%DB time两列,可以非常直观地看出当前数据库面临的主要等待事件是什么。下图两个例子分别告诉我们数据库面临锁等待和日志切换等待的情形。

    善用性能工具进行SQL整体优化

    善用性能工具进行SQL整体优化

    (5)AWR关注点5 之SQL Statistics

    SQL Statistics分别从几个维度来罗列出TOP的SQL,这是一种简单粗暴但有效的方法。看看执行时长,直接拿出来优化一般都是对的做法。

    善用性能工具进行SQL整体优化

    善用性能工具进行SQL整体优化

    (6)AWR关注点6 之Segment Statistics

    使用Segment Statistics指标进行寻找和判断,也是一个非常直接的优化手段。当我们知道繁忙落在数据库的那个表段是索引段时,优化就变得相对简单了,比如最简单粗暴的方法就是对表和索引进行数据清理和瘦身。

    善用性能工具进行SQL整体优化

    善用性能工具进行SQL整体优化

    善用性能工具进行SQL整体优化

    2ASH的关注点

    ASH是啥?哦,有人想起来了,胃镜。

    完成了ASH报告的获取后,打开获得的ASH报告,其实对于该报告可关注的东西非常直接,就是看看哪些SQL和哪些等待事件是相关联的。

    如下图所示:

    善用性能工具进行SQL整体优化

    3ADDM的关注点

    ADDM是啥?哦,是医生的门诊报告。

    由于这是Oracle的一些分析建议,所以ADDM的阅读非常简单,基本上从FINDING 1、FINDING 2顺序往下看就可以了。一般是从数据库整体配置和局部SQL两方面给出建议。我们看看都能明白,如下图所示:

    整体性的建议

    善用性能工具进行SQL整体优化

    善用性能工具进行SQL整体优化

    局部SQL建议

    善用性能工具进行SQL整体优化

    4AWRDD的关注点

    AWRDD是啥?哦,是医生在看你前后两次体检报告,在比较指标的变化。其实这个关注点很简单,基本上就是AWR关注什么,AWRDD就关注什么,没什么特别的,简单列举如下。

    (1)AWRDD关注点1 之不同时期load profile的比较

    善用性能工具进行SQL整体优化

    (2)AWRDD关注点2 之不同时期等待事件的比较

    善用性能工具进行SQL整体优化

    (3)AWRDD关注点3 之不同时期TOP SQL的比较

    善用性能工具进行SQL整体优化

    善用性能工具进行SQL整体优化

    5AWRSQRPT的关注点

    AWRSQRPT是啥?哦,有人想起来了,活检。别打颤!

    其实没啥,就是看看AWR和ASH里看不到的东西。都有啥呢?比如执行计划的相关细节,关于执行计划我们会在后面详细说明。这里要特别注意一点,Oracle的执行计划可能会随着环境的变化而变化,会随着数据的变化而变化,因此可能会产生多个执行计划,这个AWRSQRPT就会出现多个执行计划。具体详见下面系列图。

    (1)Plan statistics

    善用性能工具进行SQL整体优化

    (2)Execution Plan

    善用性能工具进行SQL整体优化

    (3)是否有多个执行计划

    善用性能工具进行SQL整体优化

    案例的分享与交流

    说了这么多,我们来看几个相关案例,体会使用工具进行整体优化的重要性。

    1、和并行等待有关的案例

    这是来自某政府系统的一个平台的案例,请看下图,这是AWR报告的Top 5 Timed Events的展现,可以看出当前数据库的等待事件主要是PX Deq相关的等待,这属于滥用并行等待导致系统资源紧张的一个案例。

    善用性能工具进行SQL整体优化

    该案例暴露出的问题比想象中更严重,因为该系统的不少表和索引的属性被设置了并行度,这导致所有对这些表和索引的访问都成了并行访问。后续解决思路就是将表和索引的并行属性去掉。将一些需要并行处理的大任务进行时间切割,确认部分大任务是可以放在凌晨业务低峰期执行的,就设置了并行的Hint任务,让部分SQL在夜间并行执行,大部分SQL在白天正常执行,从而系统恢复正常,业务也能顺利开展。

    2、和热块竞争有关的案例

    接下来我们再看一个案例,这是某运营商的系统,从AWR报告的Top 5 Timed Events等待事件主要是gc buffer busy来看,当前系统主要等待事件是热块竞争的等待。

    善用性能工具进行SQL整体优化

    等待事件对应的SQL主要有哪些,我们其实可以通过对应时间段的ASH报告分析出来,比如下图就是和AWR的对应。

    善用性能工具进行SQL整体优化

    将AWR报告和ASH报告结合起来看,往往可以找出具体需要优化的SQL。在本案例中,我们发现两个节点共同访问一些对象导致热块竞争。后续通过一系列改造,让不同的业务跑在不同的节点上,从而避免了两个节点访问同一个对象,问题得以缓解。

    3、和日志等待有关的案例

    这是一个典型的案例,从Transactions达到800多,可以看出事务非常繁忙,再从Per Transaction才1000左右,可以看出每个事务非常小。这说明了系统存在事务未批量提交的情况。这种情况一般出现在循环中,把提交写到循环里面的情况。后续通过排查,发现果真是如此原因。

    接下来的log file switch(checkpoint incomplete) 和log file sync的相关等待正是由于日志切换过于频繁导致的等待,这正是如前所述,未批量提交导致。

    善用性能工具进行SQL整体优化

    善用性能工具进行SQL整体优化

    4、新疆某系统的前台优化

    如下是新疆某运营商的优化案例,我们通过Top 5 Timed Events等待事件发现了瓶颈主要在IO。接下来我们迅速到Tablespace IO Stats模块去查看,如下图所示:

    善用性能工具进行SQL整体优化

    果然是有点问题。这个AV RD(MS)项表示平均一次物理读花费的时间(单位为ms)。有一种说法是, AV RD(MS)大于7就说明系统有严重的IO问题,其中BOSSWG_PERF_DATA居然达到了47,这说明当前的存储IO存在瓶颈。后续通过改善存储解决了问题。

    5、浙江某系统的调优案例

    这个案例来自浙江某生产系统,我们通过Top 5 Timed Events等待事件发现了瓶颈主要在gc buffer busy等待事件,这和新疆某系统的前台优化案例类似。不过AWR报告非常强大,你通过各个细节都可以很有收获,从而找到解决问题的方法,比如你此时直接定位到Segments by Global Cache Buffer Busy模块,如下图所示:

    善用性能工具进行SQL整体优化

    通过观察segments by global cache buffer busy的对象,我们找到了相关需要优化的表。最后我们结合业务,通过对该表瘦身、增加分区、避免两个节点同时访问的方案,优化了对应SQL的性能。

    总结

    善用性能工具进行SQL整体优化

    End.

    展开全文
  • 上一节深度学习计算机视觉系列(3)_线性SVMSoftMax分类器中提到两个对图像识别至关重要的概念: 用于把原始像素信息映射到不同类别得分的得分函数/score function 用于评估参数W效果(评估该参数下每类得分和实际...

    作者:寒小阳
    时间:2015年12月。
    出处:http://blog.csdn.net/han_xiaoyang/article/details/50178505
    声明:版权所有,转载请联系作者并注明出处

    1. 引言

    上一节深度学习与计算机视觉系列(3)_线性SVM与SoftMax分类器中提到两个对图像识别至关重要的概念:

    1. 用于把原始像素信息映射到不同类别得分的得分函数/score function
    2. 用于评估参数W效果(评估该参数下每类得分和实际得分的吻合度)的损失函数/loss function

    其中对于线性SVM,我们有:

    1. 得分函数f(xi,W)=Wxi
    2. 损失函数L=1Nijyi[max(0,f(xi;W)jf(xi;W)yi+1)]+αR(W)

    在取到合适的参数W的情况下,我们根据原始像素计算得到的预测结果和实际结果吻合度非常高,这时候损失函数得到的值就很小。

    这节我们就讲讲,怎么得到这个合适的参数W,使得损失函数取值最小化。也就是最优化的过程。

    2. 损失函数可视化

    我们在计算机视觉中看到的损失函数,通常都是定义在非常高维的空间里的(比如CIFAR-10的例子里一个线性分类器的权重矩阵W是10 x 3073维的,总共有30730个参数 -_-||),人要直接『看到』它的形状/变化是非常困难的。但是机智的同学们,总是能想出一些办法,把损失函数在某种程度上可视化的。比如说,我们可以把高维投射到一个向量/方向(1维)或者一个面(2维)上,从而能直观地『观察』到一些变化。

    举个例子说,我们可以对一个权重矩阵W(例如CIFAR-10中是30730个参数),可以找到W维度空间中的一条直线,然后沿着这条线,计算一下损失函数值的变化情况。具体一点说,就是我们找到一个向量W1(维度要和W一样,这样W1才能表示W的维度空间的一个方向),然后我们给不同的a值,计算L(W+aW1),这样,如果a取得足够密,其实我们就能够在一定程度上描绘出损失函数沿着这个方向的变化了。

    同样,如果我们给两个方向W1W2,那么我们可以确定一个平面,我们再取不同值的a和b,计算L(W+aW1+bW2)的值,那么我们就可以大致绘出在这个平面上,损失函数的变化情况了。

    根据上面的方法,我们画出了下面3个图。最上面的图是调整a的不同取值,绘出的损失函数变化曲线(越高值越大);中间和最后一个图是调整a与b的取值,绘出的损失函数变化图(蓝色表示损失小,红色表示损失大),中间是在一个图片样本上计算的损失结果,最下图为100张图片上计算的损失结果的一个平均。显然沿着直线方向得到的曲线底端为最小的损失值点,而曲面呈现的碗状图形碗底为损失函数取值最小处。


    损失函数沿直线投影图
    损失函数沿平面投影图2
    损失函数沿平面投影图2

    我们从数学的角度,来尝试解释一下,上面的凹曲线是怎么出来的。对于第i个样本,我们知道它的损失函数值为:


    Li=jyi[max(0,wTjxiwTyixi+1)]

    所有的样本上的损失函数值,是它们损失函数值(max(0,-),因此最小值为0)的平均值。为了更好理解,我们假定训练集里面有3个样本,都是1维的,同时总共有3个类别。所以SVM损失(暂时不考虑正则化项)可以表示为如下的式子:

    L0=L1=L2=L=max(0,wT1x0wT0x0+1)+max(0,wT2x0wT0x0+1)max(0,wT0x1wT1x1+1)+max(0,wT2x1wT1x1+1)max(0,wT0x2wT2x2+1)+max(0,wT1x2wT2x2+1)(L0+L1+L2)/3

    因为这个例子里的样本都是1维的,因此其实xiwj都是实数。拿w0举例,损失函数里,大于0的值其实都和w0是线性关系的,而最小值为0。因此,我们可以想象成,三条折线『合体』得到的最终曲线,如下图所示:
    曲线的形成

    插几句题外话,从之前碗状结构的示意图,你可能会猜到SVM损失函数是一个凸函数,而对于凸函数的最小值求解方法有很多种。但之后当我们把损失函数f扩充到神经网络之后,损失函数将变成一个非凸函数,而如果依旧可视化的话,我们看到的将不再是一个碗状结构,而是凹凸不平的曲面。

    3. 最优化

    在我们现在这个问题中,所谓的『最优化』其实指的就是找到能让损失函数最小的参数W。如果大家看过或者了解凸优化的话,我们下面介绍的方法,对你而言可能太简单了,有点原始,但是大家别忘了,我们后期要处理的是神经网络的损失函数,那可不是一个凸函数哦,所以我们还是一步步来一起看看,如果去实现最优化问题。

    3.1 策略1:随机搜寻(不太实用)

    以一个笨方法开始,我们知道,当我们手头上有参数W后,我们是可以计算损失函数,评估参数合适程度的。所以最直接粗暴的方法就是,我们尽量多地去试参数,然后从里面选那个让损失函数最小的,作为最后的W。代码当然很简单,如下:

    # 假设 X_train 是训练集 (例如. 3073 x 50,000)
    # 假设 Y_train 是类别结果 (例如. 1D array of 50,000)
    
    bestloss = float("inf") # 初始化一个最大的float值
    for num in xrange(1000):
      W = np.random.randn(10, 3073) * 0.0001 # 随机生成一组参数
      loss = L(X_train, Y_train, W) # 计算损失函数
      if loss < bestloss: # 比对已搜寻中最好的结果
        bestloss = loss
        bestW = W
      print 'in attempt %d the loss was %f, best %f' % (num, loss, bestloss)
    
    # prints:
    # in attempt 0 the loss was 9.401632, best 9.401632
    # in attempt 1 the loss was 8.959668, best 8.959668
    # in attempt 2 the loss was 9.044034, best 8.959668
    # in attempt 3 the loss was 9.278948, best 8.959668
    # in attempt 4 the loss was 8.857370, best 8.857370
    # in attempt 5 the loss was 8.943151, best 8.857370
    # in attempt 6 the loss was 8.605604, best 8.605604
    # ... (trunctated: continues for 1000 lines)

    一通随机试验和搜寻之后,我们会拿到试验结果中最好的参数W,然后在测试集上看看效果:

    # 假定 X_test 为 [3073 x 10000], Y_test 为 [10000 x 1]
    scores = Wbest.dot(Xte_cols) # 10 x 10000, 计算类别得分
    # 找到最高得分作为结果
    Yte_predict = np.argmax(scores, axis = 0)
    # 计算准确度
    np.mean(Yte_predict == Yte)
    # 返回 0.1555

    随机搜寻得到的参数W,在测试集上的准确率为15.5%,总共10各类别,我们不做任何预测只是随机猜的结果应该是10%,好像稍高一点,但是…大家也看到了…这个准确率…实在是没办法在实际应用中使用。

    3.2 策略2:随机局部搜索

    上一个策略完全就是盲搜,要想找到全局最优的那个结果基本是不可能的。它最大的缺点,就在于下一次搜索完全是随机进行的,没有一个指引方向。那我们多想想,就能想出一个在上个策略的基础上,优化的版本,叫做『随机局部搜索』。

    这个策略的意思是,我们不每次都随机产生一个参数矩阵W了,而是在现有的参数W基础上,搜寻一下周边临近的参数,有没有比现在参数更好的W,然后我们用新的W替换现在的W,接着在周围继续小范围搜寻。这个过程呢,可以想象成,我们在一座山上,现在要下山,然后我们每次都伸脚探一探周边,找一个比现在的位置下降一些的位置,然后迈一步,接着在新的位置上做同样的操作,一步步直至下山。

    从代码实现的角度看,以上的过程,实际上就是对于一个当前W,我们每次实验和添加δW,然后看看损失函数是否比当前要低,如果是,就替换掉当前的W,代码如下:

    W = np.random.randn(10, 3073) * 0.001 # 初始化权重矩阵W
    bestloss = float("inf")
    for i in xrange(1000):
      step_size = 0.0001
      Wtry = W + np.random.randn(10, 3073) * step_size
      loss = L(Xtr_cols, Ytr, Wtry)
      if loss < bestloss:
        W = Wtry
        bestloss = loss
      print 'iter %d loss is %f' % (i, bestloss)

    我们做了这么个小小的修正之后,我们再拿刚才一样的测试集来测一下效果,结果发现准确率提升至21.4%,虽然离实际应用差很远,但只是比刚才要进步一点点了。

    但是还是有个问题,我们每次测试周边点的损失函数,是一件非常耗时的事情。我们有没有办法能够直接找到我们应该迭代的方向呢?

    3.3 策略3:顺着梯度下滑

    刚才的策略,我们说了,最大的缺点是非常耗时,且计算量也很大。我们一直在做的事情,就是在当前的位置基础上,想找到一个最合适的下降方向。我们依旧回到我们假设的那个情境,如果我们在山顶,要以最快的方式下山,我们会怎么做?我们可能会环顾四周,然后找到最陡的方向,迈一小步,然后再找当前位置最陡的下山方向,再迈一小步…

    而这里提到的最陡的方向,其实对应的就是数学里『梯度』的概念,也就是说,其实我们无需『伸脚试探』周边的陡峭程度,而是可以通过计算损失函数的梯度,直接取得这个方向。

    我们知道在1个变量的函数里,某点的斜率/导数代表其变化率最大的方向。而对于多元的情况,梯度是上面情况的一个扩展,只不过这时候的变量不再是一个,而是多个,同时我们计算得到的『梯度方向』也是一个多维的向量。大家都知道数学上计算1维/元函数『梯度/导数』的表达式如下:

    df(x)dx=limh 0f(x+h)f(x)h

    对于多元的情况,这个时候我们需要求的东西扩展成每个方向的『偏导数』,然后把它们合在一块组成我们的梯度向量。

    我们用几张图来说明这个过程:


    梯度下降1
    梯度下降2
    各种下降算法

    4. 计算梯度

    有两种计算梯度的方法:

    1. 慢一些但是简单一些的数值梯度/numerical gradient
    2. 速度快但是更容易出错的解析梯度/analytic gradient

    4.1 数值梯度

    根据上面提到的导数求解公式,我们可以得到数值梯度计算法。下面是一段简单的代码,对于一个给定的函数f和一个向量x,求解这个点上的梯度:

    def eval_numerical_gradient(f, x):
      """ 
      一个最基本的计算x点上f的梯度的算法 
      - f 为参数为x的函数
      - x 是一个numpy的vector
      """ 
    
      fx = f(x) # 计算原始点上函数值
      grad = np.zeros(x.shape)
      h = 0.00001
    
      # 对x的每个维度都计算一遍
      it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
      while not it.finished:
    
        # 计算x+h处的函数值
        ix = it.multi_index
        old_value = x[ix]
        x[ix] = old_value + h # 加h
        fxh = f(x) # 计算f(x + h)
        x[ix] = old_value # 存储之前的函数值
    
        # 计算偏导数
        grad[ix] = (fxh - fx) / h # 斜率
        it.iternext() # 开始下一个维度上的偏导计算
    
      return grad

    代码的方法很简单,对每个维度,都在原始值上加上一个很小的h,然后计算这个维度/方向上的偏导,最后组在一起得到梯度grad

    4.1.1 实际计算中的提示

    我们仔细看看导数求解的公式,会发现数学定义上h是要趋于0的,但实际我们计算的时候我们只要取一个足够小的数(比如1e-5)作为h就行了,所以我们要精准计算偏导的话,要尽量取到不会带来数值计算问题,同时又能很小的h。另外,其实实际计算中,我们用另外一个公式用得更多[f(x+h)f(xh)]/2h

    下面我们用上面的公式在CIFAR-10数据集上,试一试吧:

    def CIFAR10_loss_fun(W):
      return L(X_train, Y_train, W)
    
    W = np.random.rand(10, 3073) * 0.001 # 随机权重向量
    df = eval_numerical_gradient(CIFAR10_loss_fun, W) # 计算梯度

    计算到的梯度(准确地说,梯度的方向是函数增大方向,负梯度才是下降方向)告诉我们,我们应该『下山』的方向是啥,接着我们就沿着它小步迈进:

    loss_original = CIFAR10_loss_fun(W) # 原始点上的损失
    print 'original loss: %f' % (loss_original, )
    
    # 多大步伐迈进好呢?我们选一些步长试试
    for step_size_log in [-10, -9, -8, -7, -6, -5,-4,-3,-2,-1]:
      step_size = 10 ** step_size_log
      W_new = W - step_size * df # 新的权重
      loss_new = CIFAR10_loss_fun(W_new)
      print 'for step size %f new loss: %f' % (step_size, loss_new)
    
    # 输出:
    # original loss: 2.200718
    # for step size 1.000000e-10 new loss: 2.200652
    # for step size 1.000000e-09 new loss: 2.200057
    # for step size 1.000000e-08 new loss: 2.194116
    # for step size 1.000000e-07 new loss: 2.135493
    # for step size 1.000000e-06 new loss: 1.647802
    # for step size 1.000000e-05 new loss: 2.844355
    # for step size 1.000000e-04 new loss: 25.558142
    # for step size 1.000000e-03 new loss: 254.086573
    # for step size 1.000000e-02 new loss: 2539.370888
    # for step size 1.000000e-01 new loss: 25392.214036

    4.1.2 关于迭代的细节

    如果大家仔细看上述代码的话,会发现我们step_size设的都是负的,确实我们每次update权重W的时候,是用原来的W减掉梯度方向的一个较小的值,这样损失函数才能减小。

    4.1.3 关于迭代的步长

    我们计算得到梯度之后,就确定了幅度变化最快(负梯度是下降方向)的方向,但是它并没有告诉我们,我朝着这个方向,应该迈进多远啊。之后的章节会提到,选择正确的迭代步长(有时候我们也把它叫做学习速率)是训练过程中最重要(也是最让人头疼)的一个待设定参数。就像我想以最快的速度下山,我们能感知到最陡的方向,却不知道应该迈多大的步子。如果我们小步迈进,那确实每一步都能比上一步下降一些,但是速度太慢了亲!!但是如果我们以非常非常大的步伐迈进(假如腿巨长 -_-||),那你猜怎么着,你一不小心可能就迈过山脚迈到另一座山山腰上了…

    下图是对以上情况的一个描述和解释:


    梯度下降

    图上红色的值很大,蓝色的值很小,我们想逐步下降至蓝色中心。如果迈进的步伐太小,收敛和行进的速度就会很慢,如果迈进的步伐太大,可能直接越过去了。

    4.1.4 效率问题

    如果你再回过头去看看上面计算数值梯度的程序,你会发现,这个计算方法的复杂度,基本是和我们的参数个数成线性关系的。这意味着什么呢?在我们的CIFAR-10例子中,我们总共有30730个参数,因此我们单次迭代总共就需要计算30731次损失函数。这个问题在之后会提到的神经网络中更为严重,很可能两层神经元之间就有百万级别的参数权重,所以,计算机算起来都很耗时…人也要等结果等到哭瞎…

    4.2 解析法计算梯度

    数值梯度发非常容易实现,但是从公式里面我们就看得出来,梯度实际上是一个近似(毕竟你没办法把h取到非常小),同时这也是一个计算非常耗时的算法。第二种计算梯度的方法是解析法,它可以让我们直接得到梯度的一个公式(代入就可以计算,非常快),但是呢,不像数值梯度法,这种方法更容易出现错误。so,聪明的同学们,就想了一个办法,我们可以先计算解析梯度和数值梯度,然后比对结果和校正,在确定我们解析梯度实现正确之后,我们就可以大胆地进行解析法计算了(这个过程叫做梯度检查/检测)

    我们拿一个样本点的SVM损失函数举例:

    Li=jyi[max(0,wTjxiwTyixi+Δ)]

    我们可以求它对每个权重的偏导数,比如说,我们求它对wyi的偏导,我们得到:

    wyiLi=jyi1(wTjxiwTyixi+Δ>0)xi

    其中1是一个bool函数,在括号内的条件为真的时候取值为1,否则为0。看起来似乎很吓人,但实际上要写代码完成的话,你只需要计算不满足指定SVM最小距离的类(对损失函数有贡献的类)的个数,然后用这个值会对数据向量xi做缩放即可得到梯度。但是要注意只是W中对应正确的类别的列的梯度。对于其他的jyi的情况,梯度为:

    wjLi=1(wTjxiwTyixi+Δ>0)xi

    一旦得到梯度的表达式,那计算梯度和调整权重就变得非常直接和简单。熟练掌握如何在loss expression下计算梯度是非常重要的一个技巧,贯穿整个神经网络的训练实现过程,关于这个内容,下次会详细讲到。

    5. 梯度下降

    在我们有办法计算得到梯度之后,使用梯度去更新已有权重参数的过程叫做『梯度下降』,伪代码其实就是如下的样子:

    while True:
      weights_grad = evaluate_gradient(loss_fun, data, weights)
      weights += - step_size * weights_grad # 梯度下降更新参数

    这个简单的循环实质上就是很多神经网络库的核心。当然,我们也有其他的方式去实现最优化(比如说L-BFGS),但是梯度下降确实是当前使用最广泛,也相对最稳定的神经网络损失函数最优化方法。

    5.1 Mini-batch gradient descent

    在大型的应用当中(比如ILSVRC),训练数据可能是百万千万级别的。因此,对整个训练数据集的样本都算一遍损失函数,以完成参数迭代是一件非常耗时的事情,一个我们通常会用到的替代方法是,采样出一个子集在其上计算梯度。现在比较前沿的神经网络结构基本都是这么做的,例如ConvNets是每256张作为一个batch去完成参数的更新。参数更新的代码如下:

    while True:
      data_batch = sample_training_data(data, 256) # 抽样256个样本作为一个batch
      weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
      weights += - step_size * weights_grad # 参数更新

    之所以可以这么做,是因为训练数据之间其实是关联的。我们简化一下这个问题,你想想,如果ILSVRC中的120w图片,如果只是1000张不同的图片,一直复制1200次得到的。那么其实我们在这1000张图片上算得的损失函数和120w的平均其实是一致的。当然,当然,在实际场景中,我们肯定很少遇到这种多次重复的情况,但是原数据的一个子集(mini-batch)上的梯度,其实也是对整体数据上梯度的一个很好的近似。因此,只在mini-batch上计算和更新参数,会有快得多的收敛速度。

    上述算法的一个极端的情况是,如果我们的一个mini-batch里面只有一张图片。那这个过程就变成『随机梯度下降/Stochastic Gradient Descent (SGD)』,说起来,这个其实在实际应用中倒也没那么常见,原因是向量化之后,一次计算100张图片,其实比计算一张图片100次,要快得多。所以即使从定义上来说,SGD表示我们用一张图片上的梯度近似全局梯度,但是很多时候人们提到SGD的时候,其实他们指的是mini-batch梯度下降,也就是说,我们把一个batch当做1份了。额,还要稍微提一句的是,有些同学可能会问,这个batch size本身不是一个需要实验的参数吗,取多大的batch size好啊?但实际应用中,我们倒很少会用cross-validation去选择这个参数。这么说吧,我们一般是基于我们内存限制去取这个值的,比如设成100左右。

    6. 总结

    • 把损失函数在各参数上的取值,想象成我们所在山峰的高度。那么我们要最小化损失函数,实际上就是『要想办法下山』。
    • 我们采取的下山策略是,一次迈一小步,只要每次都往下走了,那么最后就会到山底
    • 梯度对应函数变化最快的方向,负梯度的方向就是我们下山,环顾四周之后,发现最陡的下山路方向。
    • 我们的步长(也叫学习率),会影响我们的收敛速度(下山速度),如果步伐特别特别大,甚至可能跃过最低点,跑到另外一个高值位置了。
    • 我们用mini-batch的方式,用一小部分的样本子集,计算和更新参数,减少计算量,加快收敛速度。

    参考资料与原文

    cs231n 最优化与随机梯度下降

    展开全文
  • 解析Oracle数据扫描 Oracle SQL查询优化 (2) 2、 提高局部范围数据扫描执行性能的原理: 2.1查询条件的类别作用:  当我们发起一个带有条件的查询SQL语句时,通常会赋予该语句多个查询条件,在这些查询...

    解析Oracle数据扫描 Oracle SQL查询优化

    (2)

    2、 提高局部范围数据扫描执行性能的原理:

    2.1查询条件的类别与作用:

          当我们发起一个带有条件的查询SQL语句时,通常会赋予该语句多个查询条件,在这些查询条件中,通常会有一个或者几个查询条件会作为数据检索的发起者,这些作为发起者的查询条件称为“驱动查询条件”;同时那些不作为数据检索发起者的查询条件,在整个查询数据的过程中,只会起到过滤由驱动查询条件所决定的数据范围内数据的作用,这些查询条件就称为“过滤查询条件”。

          由此可见驱动查询条件和过滤查询条件,一个起到查询发起者的作用,一个起到数据过滤者的作用,二者操作的目标数据集是不同的,驱动查询条件查询的是整体数据集,而过滤查询条件查询的是由驱动查询条件获取的结果集。由此可见驱动查询条件对于最终的查询语句的执行性能,起到了至关重要的作用。因为他们决定了最终查询结果的最大数据范围,以及能否最快的获取这个范围内的数据,而过滤查询条件只是从驱动查询条件决定的数据范围内进行数据过滤。当然我们也不能忽视通过过滤查询条件进行数据过滤对查询性能的影响,但是毕竟它没有对最终性能起到主要作用。

         通常选择哪个查询条件作为驱动查询条件是由执行优化器和优化器运行模式(FIRST_ROWS或者ALL_ROWS)决定的。Oracle在决定驱动查询条件时,通常会考虑使用具有索引的列作为驱动查询条件,一般情况下Oracle会评估个查询条件上是否具备主键索引、唯一索引、B*Tree或其他类型索引、以及是否是使用等值条件的复合索引的手字段等,同时还会基于优化器模式通过成本估算方法评估各种索引的成本效能,尽力从中识别出最为高效的索引并将其作为驱动查询条件。如果查询优化器发现存在多个高效索引,那么理论上优化器会使用自己的策略决定出最佳执行成本的驱动查询条件;同时查询优化器还会识别出,哪些本应用于过滤条件的索引项,虽然不是那么高效,但是对驱动查询条件检索数据会提供很好的帮助,那么优化器会将这些索引与驱动查询条件合并执行,进而期望得到最好的查询性能。

         但是通过查询优化器来决定驱动查询条件时,通常情况下都是没问题的,但是有些时候由于数据分布、数据对象统计信息过旧,以及与我们期望的数据扫描方式不符等问题,不得不需要我们人为去干预上述优化器的决策过程,其中最常用的干预手段就是使用Hints。这也从一个侧面说明,当今人工智能还无法取代真正的人类智能。

    2.2提升范围扫描性能的原则:

          有了上述查询条件类别的知识,我们就可以来讨论提升数据局部范围扫描性能的原则了。即使从常识出发来思考,也可以知道从较小的数据集中查询数据的执行速度会比较高效,从大范围的数据集中查询数据会比较低效。但是一旦融入了上述驱动查询条件和过滤查询条件的相互作用,在局部范围扫描时就会发现,从较大范围中查询数据的输出速度反倒高于从较小范围中查询数据。为什么会得到这样与我们常识相反的结果呢?我们通过下面的例子来说明这个问题。

    这里有一个普通的SQL查询语句

    Select * from order where ordno between 1and 1000 and custno like ‘TB%’;

          假设在ordno和custno字段上都建有索引,同时知道满足ordno查询条件的数据记录有1000行,满足custno的数据记录有10行。很明显由于查询条件都具有索引,而且无需排序、分组、汇总等操作,因此该语句会使用局部范围数据扫描,那么就可能出现如下执行路径:

    (1)   使用ordno作为驱动查询条件,custno作为过滤查询条件:

           此时查询会使用ordno索引扫描数据满足条件的数据行,同时使用custno过滤条件来过滤满足条件的数据行,并将过滤出的符合条件的数据行填充到批量数组中,作为查询结果返回。但是问题是满足ordnobetween 1 and 1000条件的记录行有1000行,而满足custnolike ‘TB%’条件的记录行只有10行,那么用只有10行记录的数据去过滤拥有1000行记录的数据,并填充容纳最终结果的批量数组,可想而知批量数组很难被快速填满,即查询结果很难快速被返回,最坏的情况下可能要从头到尾的扫描驱动查询条件所决定的全部数据范围(1000条记录)后查询才能结束。

    (2)   使用custno作为驱动查询条件,使用ordno作为过滤查询条件:

          此时查询使用custno索引扫描满足条件的数据行,同时使用ordno过滤条件来过滤满足条件的数据行,并将过滤出的符合条件的数据行填充到批量数组中,作为查询结果返回。这时由于满足ordnobetween 1 and 1000条件的记录行有1000行,那么很容易从中找到能够匹配满足custnolike ‘TB%’条件的10行记录,因此批量数组很容易就能被填满,即使最坏的情况也最多只会扫描满足custnolike ‘TB%’条件的10行记录,因此此时查询结果接能够快速的返回。

         为了改进查询性能,我们可以使用交换查询条件角色的方式,即交换驱动查询条件和过滤查询条件,因此我们可以使用下面的语句来改写上述查询:

    Select /*+ index(order custno_inx) */ *

    From order where ordnobetween 1 and 1000 and custno like ‘TB%’;

    我们通过Hints来影响优化器行为,使优化器制定出从使用custno字段上的索引来发起查询,即将驱动查询条件变成custnolike ‘TB%’,将过滤查询条件变成ordno between 1 and 1000。这里使用了Hints,其实还可以使用一些措施使得ordno索引失效,也可以达到同样的目的,比如在ordno字段上使用一个对查询结果不会造成影响的函数,如rtrim(ordno)between1 and 1000,但这种方法存在弊端,它可能会影响优化器制定出合并使用custno和ordno共同来进行数据查询的执行计划。

         从这个示例中我们可以看出提升局部范围扫描执行性能的策略有以下两点:

    Ø  尽量缩减驱动查询条件的数据范围,以减少数据扫描量;

    Ø  尽量扩大过滤查询条件的数据范围,以增加填满批量数组快速返回查询结果的概率;

    由此我们可以得到下述局部范围扫描性能提升策略描述矩阵:

    满足驱动查询条件的数据范围

    满足过滤查询条件的数据范围

    性能

    措施

     

     

    交换过滤条件的角色

     


    展开全文
  • [架构优化]webAPP优化解决办法

    万次阅读 2017-01-02 11:41:37
    reflow分为局部回流全局回流,会影响下面的,不会影响上面的元素 reflow耗用的系统资源较大,DOM Tree中受到影响的节点皆会reflow,然后影响其子节点最坏的情况是所有节点reflow,该问题引发的现象便是低性能的...
  • 关于优化

    2008-07-09 01:37:00
    优化分为三个层次:架构级的,流程逻辑和算法级的,以及技巧级的。在这三个级别中,就对整体的性能影响而言,前两个级别难分伯仲...而技巧级的优化,其影响则往往仅局限于某一局部,虽然很有可能是很关键的局部。但既然
  • 窥孔优化

    千次阅读 2017-07-01 22:20:52
    一种很局部优化方式,编译器仅仅在一个基本块或者多个基本块中,针对已经生成的代码,结合CPU自己指令的特点,过一些认为可能带来性能提升的转换规则,或者通过整体的分析,通过指令转换,提升代码性能。...
  • 梯度下降优化算法总结

    万次阅读 多人点赞 2017-07-21 22:44:54
    本次介绍梯度下降优化算法。主要参考资料为一篇综述《An overview of gradient descent optimization algorithms》
  • 对于凸优化来说,局部最优就是全局最优,换句话说,极小值就是最小值。 至于为什么?这个就是数学证明了,这个要用到凸函数、凸集的定义。 我们可以用反证法来证明。已知x0x0是个局部最优点,假设在全集SS上存在一...
  • PyTorch学习之十种优化函数

    万次阅读 多人点赞 2019-01-01 11:29:08
    1 使用 2 基类 Optimizer torch.optim.Optimizer(params, defaults) ...defaults —— (dict):包含了优化选项默认值的字典(一个参数组没有指定的参数选项将会使用默认值)。 load_state_dict(stat...
  • 梯度下降优化算法综述

    万次阅读 多人点赞 2017-04-14 17:28:56
    然而,已经证明当我们缓慢减小学习率,SGD批梯度下降法具有相同的收敛行为,对于非凸优化和凸优化,可以分别收敛到局部最小值和全局最小值。批梯度下降的代码相比,SGD的代码片段仅仅是在对训练样本的遍历和利用...
  • 优化算法——遗传算法

    万次阅读 多人点赞 2015-05-10 17:09:28
    遗传算法的第一次接触 遗传算法的基本概念 基本定义 遗传算法的基本流程 遗传算法过程中的具体操作 参数的编码 二进制编码 Gray编码 实数编码 有序编码 初始群体的设定 适应度函数的计算 遗传操作设计 选择...
  • order表上的ord_date和agent_cd字段上存在组合索引,那么该语句仍然无法实现只通过索引执行局部扫描来获取数据,而是通过ord_date like ‘201110%’驱动条件扫描ord_date和agent_cd字段上的组合索引获取数整体范围,...
  • 视觉SLAM笔记(56) 位姿图优化

    万次阅读 2019-11-16 10:02:08
    g2o 原生位姿图、李代数上的位姿图优化
  • Web业务性能优化技术总结

    千次阅读 2017-03-18 01:00:25
    Web业务的性能优化是一个系统工程,既有深度,又有广度。以下所简称性能均特指Web业务性能。 技术的广度上,主要从大背景下考虑到其各个相关方,基于共同的数据指标发掘和评估方案。 技术的深度上是一个渐进和迭代的...
  • 我们知道,梯度下降算法是利用梯度进行一阶优化,而今天我介绍的牛顿优化算法采用的是二阶优化。本文将重点讲解牛顿法的基本概念和推导过程,并将梯度下降牛顿法做个比较。 1. 牛顿法求解方程的根 有时候,在...
  • iOS之性能优化·优化App的启动速度

    千次阅读 2020-11-11 04:16:35
    苹果是一家特别注重用户体验的公司,过去几年一直在优化 App 的启动时间,特别是去年的 WWDC 2019 keynote [1] 上提到,在过去一年苹果开发团队对启动时间提升了 200%; 虽然说是提升了 200%,但是有些问题还是没有...
  • 优化与过度优化 弱依赖,强依赖与过度依赖 整体与部分 优化与过度优化 性能优化,是指性能出现问题,流量出现瓶颈才进行的优化,不要出现优化而优化 过度优化,这个词可能出现在SEO里面比较多一些,但是在实际行为...
  • 在 Android 开发中, 内存优化是APP性能优化中很重要的一个部分. 而在内存优化中, 最重要的就是修复内存泄漏问题. 本文就来介绍一下内存泄漏的基本概念以及常用的检测手段.1. 什么是内存泄漏简单来说, 当一个对象不再...
  • 数据库SQL优化大总结之 百万级数据库优化方案

    万次阅读 多人点赞 2016-06-23 09:43:50
    网上关于SQL优化的教程很多,但是比较杂乱。近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。 这篇文章我花费了大量的时间查找资料、修改、排版,希望大家阅读之后,感觉好的...
  • Android性能优化全方面解析

    千次阅读 2017-05-15 11:47:59
    一、性能优化。二、高级UI。三、JNI/NDK开发。四、架构师。五、RN开发。这也许将会是我的进阶趋势。早已知道在瓶颈期的我,似乎看到了突破的希望的。初级进阶中级也好,中级进阶高级也罢,现在的市场无非是根据经验...
  • Webpack性能优化

    千次阅读 2019-08-27 11:07:12
    Webpack是现在主流的功能强大的模块化打包工具,在使用Webpack时,如果不注意性能优化,有非常大的可能会产生性能问题,性能问题主要分为开发时打包构建速度慢、开发调试时的重复性工作、以及输出文件质量不高等,...
  • 第七章、网络优化与正则化

    千次阅读 2020-08-23 15:28:21
    用神经网络模型依然存在一些难点问题: (1) 优化问题 神经网络的损失函数是一个非...在网络优化方面,介绍一些常用的优化算法、参数初始化方法、数据预处理方法、逐层归一化方法和超参数优化方法.在网络正则化方面,
  • 在对函数进行凸优化时,如果使用导数的方法(如:梯度下降法/GD,牛顿法等)来寻找最优解,有可能陷入到局部最优解而非全局最优解。 为了防止得到局部最优,可以对梯度下降法进行一些改进,防止陷入局部最优。 ...
  • 常见优化算法

    千次阅读 2017-09-28 18:45:40
    在机器学习的模型优化求解中必然用到优化算法,其地位在机器学习领域不可小觑。本文将对常见的优化算法进行简单总结。
  • TEB轨迹优化算法-代码解析参数建议

    千次阅读 多人点赞 2020-03-06 11:41:50
    TEB算法总结 1. 简介 ​ “TEB”全称Time ...在轨迹优化过程中,该算法拥有多种优化目标,包括但不限于:整体路径长度、轨迹运行时间、障碍物的距离、通过中间路径点以及机器人动力学、运动学以及几何约束的符...
  • 数据库sql优化

    千次阅读 多人点赞 2018-07-06 15:55:30
    网上关于SQL优化的教程很多,但是比较杂乱。近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。这篇文章我花费了大量的时间查找资料、修改、排版,希望大家阅读之后,感觉好的...
  • 视觉SLAM笔记(52) BA 优化

    万次阅读 2019-11-15 20:17:33
    视觉SLAM笔记(52) BA 优化 1. Bundle Adjustment 2. 投影模型和 BA 代价函数 3. BA 的求解 4. 稀疏性和边缘化 5. 鲁棒核函数 1. Bundle Adjustment 在 视觉SLAM笔记(36) 3D-2D: PnP 中提及到了BA ...
  • 优化问题综述

    万次阅读 多人点赞 2017-03-19 18:58:47
    优化问题一般可分为两大类:无约束优化问题和约束优化问题,约束优化问题又可分为含等式约束优化问题和含不等式约束优化问题。 无约束优化问题 含等式约束的优化问题 含不等式约束的优化问题 2 求解策略 针对...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 66,661
精华内容 26,664
关键字:

局部优化与整体优化