性能之巅:洞悉系统、企业与云计算——应用程序

性能调整离工作所执行的地方越近越好:最好在应用程序里。应用程序包括数据库、Web服务器、应用服务器、负载均衡器、文件服务器,等等。

应用程序能变得极其复杂,尤其是涉及众多组件的分布式应用程序环境中。研究应用程序的内部通常是应用程序开发人员的领域,会涉及第三方工具自测。对于研究系统性能的人员,包括系统管理员,应用程序性能分析包括配置应用程序实现系统资源最佳利用、归纳应用程序使用系统的方式,以及场景问题的分析。

应用程序基础

在深入研究应用程序性能之前,我们应该了解应用程序的职能、它的基础特征,以及它在业界的生态系统。这组成了我们理解应用程序活动的上下文。这是学习常见性能问题和性能调整的好机会,还为我们的进一步学习指明了道路。要学习这个上下,试着回答下面这些问题。

  • 功能:应用程序的角色是什么?是数据库、Web服务器、负载均衡器、文件服务,还是存储对象?
  • 操作:应用服务器有哪些请求,或者执行怎么样的操作?数据库服务器查询、Web服务器服务HTTP请求,等等。这些可以用速率来度量,用以估计负载和做容量规划。
  • CPU模式:应用程序是用户级的软件实现还是内核级的软件实现?多数的应用程序是用户级别的,以一个或多个进程的形式执行,但是有些是以内核服务的形式实现的(例如,NFS)。
  • 配置:应用程序是怎样的配置,为什么这么配?这些信息能在配置文件里找到或者用管理工具得到。检查所有与性能相关的可调参数有没有改过,包括缓冲区大小、缓存大小、并发(进程或线程),以及其他选项。
  • 指标:有没有可用的应用程序指标,如操作率?可能是用自带工具或第三方工具,通过API请求,或者通过处理操作日志得到。
  • 日志:应用程序创建的操作日志是哪些?能启用什么样的日志?哪些性能指标,包括延时,能从日志中得到?例如,MySQL支持慢日志(slow quey log),对每一个特定阀值的请求提供有价值的性能细节信息。
  • 版本:应用程序是最新的版本吗?在最近的版本的发布说明里有没有提及性能的修复和性能的提示?
  • Bugs:应用程序有bug数据库吗?我们用的应用程序有什么样的“性能”bug?如果当前有一个性能问题,查找bug数据库看看以前有没有发生过类似的事情,看看是怎么样调查的,以及还有没有涉及其他内容。
  • 社区:应用程序社区里有分享性能发现的地方吗?如论坛、博客、聚合和会议。
  • :有与应用程序以及它的性能相关的书吗?
  • 专家:谁是这个应用程序公认的性能专家。

除了这些资源,我们要致力于高层次地理解应用程序——它的作用、怎样操作、怎样执行。

目标

设立性能目标能为我们的性能分析工作指明方向。并帮助我们选择要做的事情。没有清晰的目标,性能分析容易沦为随机的“钓鱼探险”。

关于应用程序的性能,可以从应用程序执行什么操作和要实现怎么样的性能目标入手。目标可能如下:

  • 延时:低应用程序响应时间
  • 吞吐量:高应用程序操作率或者数据传输率
  • 资源使用率:对于给定应用程序工作负载,高效地使用资源

如果上述这些目标可量化,就更好了,用从业务或者服务质量需求衍生出的指标做量化,例如:

  • 应用程序平均延时5ms
  • 95%的请求的延时在100ms或以下
  • 消灭延时异常值,超过1000ms延时的请求数为0
  • 最大吞吐量为每台服务器最少10 000次应用请求 / 秒
  • 在每秒10 000次应用请求的情况下,平均磁盘使用率在50%以下

一旦选中一个目标,就能着手处理阻碍该目标实现的限制因素了,对于延时而言,限制因素可能是磁盘或网络I/O;对于吞吐量,可能会是CPU。

整单基于吞吐量的目标,要注意就性能或开销而言,所有的操作并不都是一样的。如果目标是提高操作的速率,那么识别操作是怎样的类别就很重要了,针对所期望或所测量的工作负载,该操作可能会存在一个分布。

常见情况的优化

软件的内部可能是很复杂,有许多不同的可能代码路径和行为。当浏览源代码时尤其明细:应用程序一般是万行级,操作系统内核则上至百万行级。随机地找地方做优化会事倍功半。

一个能有效提高应用程序性能的方法是找到对应生产环境工作负载的公用代码路径,并开始对其做优化。如果应用程序是CPU密集型的,那么意味着代码路径会频繁占用CPU。如果应用程序是I/O密集型,就应该查看导致频繁I/O的代码路径。这些都能通过分析和剖析应用程序来确定。

观测性

操作系统最大的性能提升在于消除不必要的工作。对于应用程序来说也是一样的。这一事实有时会被忽视,尤其当应用程序以性能选择为基准时。如果基准测试显示应用程序A比应用程序B快10%,那么A会是很有诱惑力的选择。但是,如果应用程序A是不透明的,而应用程序B提供了一套丰富的观测工具,那么对于长期运行来说,应用程序B是更换的选择。那些观测工具可以让人看到并进而消除不必要的工作,能更好地理解并调整运行的工作。通过增强观测能力而获得的性能收益让最初的10%的性能差异显得微不足道。

大O标记法

大O标记法一般用于计算机科学学科的教学,用于分析算法的复杂度,以及随着输入数据集的增长对算法的执行情况建模。这有助于程序员在开发应用程序时,选择用于更高效率和性能的算法。

大O标记的示列

标记法示列
O(1)布尔判断
O(log n)顺序队列的二分搜索
O(n)链表的线性搜索
O(n log n)快速排序(一般情况)
O(n^2)冒泡排序(一般情况)
O(2^n)分解质因数;指数增长
O(n!)旅行商人问题的穷举法

这个标记法能让程序员估计不同算法的速度,判断代码的哪些地方能引起最大的改进。

这样的分类让系统性能分析人员理解某些算法在扩展的时候性能会很差。当应用程序被迫服务比之前更高的用户数或更多的数据对象时可能会出现性能问题,此时诸如O(n^2) 的算法就是根源所在。开发人员要使用高效的算法或者对输入做程序切分来修复这个问题。

在这里插入图片描述

应用程序性能技术

提供应用程序性能的常用技术:选择I/O大小、缓存、缓冲区、轮询、并发和并行、非阻塞I/O和处理器绑定。

选择I/O尺寸

执行I/O的开销包括初始化缓冲区、系统调用、上下文切换、分配内核元数据、检查进程权限和限制、映射地址到设备、执行内核和驱动代码来执行I/O,以及,在最后释放元数据和缓冲区。“初始化开销”对于小型和大型的I/O都是差不多的。从效率上来说,每次I/O传输的数据越多,效率越高。

增加I/O尺寸是应用程序提高吞吐量的常用策略。考虑到每次I/O的固定开销,一次I/O传输128KB要比128次传输1KB高效得多。尤其是磁盘I/O,由于寻到时间,每次I/O开销都较高。

如果应用程序不需要,更大的I/O尺寸也会带来负面效应。一个执行8KB随机读取的数据库按128KB I/O的尺寸运行会慢得多,因为128KB的数据传输能力被浪费了。选择小一些的I/O尺寸,更贴近应用程序所需,能减低引起的I/O延时。不必要的大尺寸I/O还会浪费缓存空间。

缓存

操作系统用缓存提高文件系统的读性能和内存的分配性能,应用程序使用缓存也出于类似的原因。将经常执行的操作的结果保持在本地缓存中以备后用,而非总是执行开销较高的操作。数据库缓冲区高速缓存就是一例,该缓存会保存经常执行的数据库查询结果。

部署应用程序时,一个操作的操作就是决定用什么样的缓存,或能启用什么样的缓存,然后配置适合系统的缓存尺寸。

缓存一个重要的方面就是如何保证完整性,确保查询不会返回过期的数据。这称为缓存一致性(cache coherency),而且执行的代价不低——理想情况下,不要高于缓存所带来的益处。

缓存提高了读操作性能,存储通常用缓冲区来提高写操作的性能。

缓冲区

为了提高写操作性能,数据在送入下一层级之前会合并放在缓冲区。这增加了I/O大小,提升了操作的效率、取决于写操作的类型,这样做可能会增加写延时,因为第一次写入缓冲区后,在发送之前,还要等到后续的写入。

环形缓冲区(或循环缓冲区)是一类用于组件之间连续数据传输的大小固定的缓冲区,缓冲区的操作是异步的。该类型缓冲可以用头指针和尾指针来实现,指针随着数据的增加或移出而改变位置。

轮询

轮询是系统等待某一事件发生的技术,该技术在循环中检查事件状态,两次检查之间有停顿。轮询有一些潜在的性能问题:

  • 重复检查的CPU开销高昂
  • 事件发生和下一次检查的延时较高

这是性能问题,应用程序应能改变自身行为来监听事件发生,当事件发生时立即通知应用程序并执行相应的程序

poll()系统调用

有系统调用poll() 来检查文件描述符的状态,提供与轮询相似的功能,不过它是基于事件的,因此没有轮询那样的性能负担。

poll() 接口支持多个文件描述符作为一个数组,当事件发生要找到相应的文件描述符时,需要应用程序扫码这个数组。这个扫描是O(n),扩展时可能会变成一个性能问题:在Linux里面是epoll(),epoll() 避免了这种扫描,复杂度是O(1)。

并发和并行

分时系统(包括所有从UNIX衍生的系统)支持程序的并发:装载和开始执行多个可运行程序的能力。虽然它们的运行时间是重叠的,但并不一定在同一瞬间都在CPU上执行。每一个这样的程序都可以是一个应用程序进程。

除了并发执行不同的应用程序,应用程序内的函数也可以是并发的。这可以用多进程或多线程实现,每个进程/线程都执行自己的认为。

另一个方法是基于事件并发(event-based concurrency),应用程序服务于不同的函数并在事件发生时在这些函数之间进行切换。例如,Node.js 采用的就是一方法。这种方法提供了并发性但可能用的是单个的线程或进程,最终会成为一个制约性扩展性的瓶颈,因为它只利用了一个CPU。

为了利用多处理器系统的优势,应用程序需要在同一时间运行在多个CPU上。这称为并行,应用程序通过多进程或多线程实现。多线程(或多任务)更为高效,因此也是首选的方法。

除了增加CPU工作的吞吐量,多线程(或多进程)让I/O可以并发执行,当一个线程阻塞在I/O等待的时候,其他线程还能执行。

因为多线程编程能共享同一进程内地址空间,因此线程可以直接对同一内存读取和写入,而不需要代金更高昂的接口(例如,多进程编程里的进程间通信 IPC)。可使用同步原语来保障完整性,这样数据就不会因为多线程同时读写而损坏。这些一般会与哈希表一同使用来提高性能。

同步原语

同步原语(synchronization primitive)监控内存的访问,与交通等控制十字路口的访问方式相同,正如红绿灯一样,它们停止交通的流动,引起等待时间(延时)。常见的三种类型如下:

  • mutex(MUTually EXclusive)锁:只有锁的持有者才能操作,其他线程会阻塞并等待CPU
  • 自旋锁:自旋锁允许锁持有者操作,其他的需要自旋锁的线程会再CPU上循环自旋,检查锁是否被释放。虽然这样可以提供低延时的访问,被阻塞的线程不会离开CPU,时刻准备着运行直到锁可用,但是线程自旋、等待也是对CPU资源的浪费。
  • 读写锁:读写锁通过允许多个读者或者只允许一个写者而没有读者,来保证数据的完整性。

哈希表

可以用一张锁的哈希表来对大量的数据结构锁做数目优化。哈希表的内容将总结于此:

下面是两种方法:

  • 为所有的数据结构只设定一个全局的mutex锁。虽然这个方案很简单,不过并发的访问会有锁的竞争,等待时也会有延时。需要该锁的多个线程会串行执行,而不是并发执行。
  • 为每个数据结构都设定一个mutex锁。虽然这个方案将锁的竞争减小到真正需要时才发生——对同一个数据结构的访问也会是并发的——但是锁会有存储开销,为每个数据结构创建和销毁锁也会有CPU开销。

锁哈希表是一种折中方案,当期望锁的竞争能轻一些的时候很适用。创建固定数目的锁,用哈希算法来选择哪个锁用于哪个数据结构。这就避免了随数据结构创建和销毁的开销,也避免了只使用单个锁的问题。

非阻塞I/O

  • 对于多路并发的I/O,当阻塞时,每一个阻塞的I/O都会消耗一个线程或进程。为了支持多路并发I/O,应用程序必须创建很多的线程(通常一个客户端一个线程),伴随着线程的创建和销毁,这样做的代价也很大
  • 对于频繁发生的短时I/O,频繁切换上下文的开销会消耗CPU资源并增加应用程序的延时

非阻塞I/O模型是异步地发起I/O,而不阻塞当前的线程,线程可以执行其他的工作。这是Node.js的一个关键特性,Node.js是服务器端的JavaScript应用程序环境,可以用非阻塞的方式开发程序。

处理器绑定

NUMA环境对于进程或线程保持运行在一个CPU上是有优势的,线程执行I/O后,能像执行I/O之前那样运行在同一CPU上。这提供了应用程序的内存本地性,减少内存I/O,并提高了应用程序的整体性能。操作系统对此是很清楚的,设计的本意就是让应用程序线程依附在同一个CPU上。

某些应用程序会强制将自身与CPU绑定。对于某些系统,这样做能显著地提高性能。不过如果这样的绑定与其他CPU的绑定冲突,如CPU上的设备中断映射,这样的绑定就会损害性能。

如果还有其他应用程序运行在统一系统上,要及其消息CPU绑定带来的风险。

编程语言

编程语言可能是编译的或是解释的,也有可能是通过虚拟机执行的。许多语言都将“性能优化”作为一个特性,但是,严格来将,这个特别是执行该语言的软件的特性,而不是语言本身的。

解释器和语言虚拟机有自己专门的工具,做不同级别的性能观测。对于系统性能分析来说,用这些工具做简单的剖析就能很快得到结果。例如,高CPU使用率可能是由垃圾回收(GC)导致的,用某些常用的可调参数就能解决这一问题;也可能是由某一代码路径导致的,在bug数据库里可以找到已知的bug,升级软件版本就能修复。

编译语言

编译是在运行之前将程序生成机器指令,保存在二进制可执行文件里。这些文件可用在任何时间运行而无须在读编译。

编译过的代码总体来说是高性能的,在被CPU执行之前不需要进一步转换。

解释语言

解释语言程序的执行是将语言在运行时翻译成行为,这一过程会增加执行开销。解释语言并期望能表现出很高的性能,而是用于其他因素更重要的情况下,诸如易于编程和调试。

除非提供了专门的观测工具,否则对解释语言做性能分析是很困难的。CPU剖析能展示解释器的操作——包括分句、翻译和执行操作——但是不能显示原始程序的函数名,关键程序上下文仍然是个迷。

不过对解释器的分析并不是毫无结果的,因为解释器本身也可能有性能问题,尤其是确信所执行的代码是设计精良的时候。

依靠解释器,程序上下文能容易地直接得到(例如,对分词器做动态跟踪)。我们常常通过简单的打印语句和时间戳来研究这些程序。更严格的性能分析并不场景,因为解释语言一般不是编写高性能应用程序的首选。

虚拟机

语言虚拟机,或称为进程虚拟机是模拟计算机的软件,一些编程语言,如Java和Erlang,都是用虚拟机(VM)执行,提供了平台独立的编程环境。应用程序先编译成虚拟机指令集(字节码,bytecode),再由虚拟机执行。这样编译的对象就具有了可移植性,只要有虚拟机就能在目标平台上运行这些程序。

虚拟机一般是语言类型里面最难观测的。在程序执行在CPU上之前,多个编译或解释的阶段都可能已经过去的,关于原始程序的信息也可能没有现成的。性能分析通常靠的是语言虚拟机提供的工具集合第三方的工具。

垃圾回收

一些语言使用自动内存管理,分配的内存不需要显式地释放,留给异步的垃圾收集来处理。虽然这让程序更容易编写,但也有缺点,如下所示:

  • 内存增长:针对应用程序内存使用的控制不多,当没能自动识别出对象适合被释放时内存的使用会增加。如果应用程序用的内存变变得太大,达到了程序的极限或者引起了系统换页,就会严重损害性能。
  • CPU成本:GC通常会间歇地运行,还会搜索和扫码内存中的对象。这会消耗CPU资源,短期内能供给应用程序的可用的CPU资源就变少了。随着应用程序使用的内存增多,GC对CPU的消耗也会增加。在某些情况下,可能会出现GC不断地消耗整个CPU的现象。
  • 延时异常值:GC执行期间应用程序的执行可能会中止,偶尔出现高延时的响应。这也取决于GC的类型,全停、增量,或是并发。

GC是常见的性能调整对象,用以降低CPU成本和减少延时异常值的发生。如果调整参数没有效果,那么问题可能就是应用程序创建了太多的垃圾,或者引用泄露。这些都是应用程序开发人员的问题。

方法和分析

应用程序性能方法

方法类型
线程状态分析观测分析
CPU剖析观测分析
系统调用分析观测分析
I/O剖析观测分析
工作负载特征归纳观测分析,容量分析
USE方法观测分析
向下挖掘分析法观测分析
锁分析法观测分析
静态性能调优观测分析,调优

这些方法可以单独使用,也可以合在一起使用。建议是按表中所列顺序尝试这些方法。

除此之外,针对特定的应用程序和开发该应用程序所用的编程语言,寻求定制化分析技术。要做到这些需要考虑应用程序的逻辑行为,包括已知的问题以及所能获得的性能收益。

线程状态分析

线程状态分析的目的是分辨应用程序线程的时间用在了什么地方,这能用来很快地解决某些问题,并给其他问题的研究指明放心。通常将应用程序的时间分成几个具有实际意义的状态。

两种状态
最少,线程分为以下两个状态:

  • on-CPU:执行
  • off-CPU:等待下一轮上CPU,或者等待I/O、锁、换页、工作,等等。

如果时间大量的花在CPU上了,CPU的剖析能很快地解释这一点。许多性能问题都是这种情况,因此并没有必要花时间在其他状态的测量上。

如果发现时间没有花在CPU上,还有其他多种的方法可以使用,不过如果没有一个很好的研究起点,做起来可能会费时。

六种状态
下面是一个扩展的列表,这次用的是六种线程状态(不同的命名方式),将off-CPU上的情况做了更好的描述。

  • 执行:在CPU上。
  • 可运行:等待轮到上CPU。
  • 匿名换页:可运行,但是因等待匿名换页而受阻。
  • 睡眠:等待包括网络、块设备和数据/文本换入在内的I/O。
  • :等待获取同步锁(等待其他线程)。
  • 空闲:等待工作。

这个集合的选择保证了最小性和有用性,我们可以把更多的状态加入我们列表中。例如,执行的状态可以分为用户态和内核态执行,休眠的状态可以根据休眠的目标来做划分。

通过减少这些状态中的前五项的时间,会得到性能提升,同时也会增加空闲的时间。若其他情况不变,这就意味着应用程序请求的延时变小。应用程序能应对更多的负载。

一旦确定了线程在前五个状态中花的世界,就可以做如下的进一步的研究:

  • 执行:检查执行的是用户态时间还是内核态时间,用剖析来做CPU资源消耗分析。培训可以确定哪些代码路径消耗CPU和消耗了多久,其中包括花费在自旋锁上的时间。
  • 可运行:在这个状态上耗时意味着应用程序需要更多的CPU资源。检查整个系统的CPU负载,以及所有对该应用程序做的CPU限制(例如,资源控制)
  • 匿名换页:应用程序缺少可用的主存会引起换页和延时。检查整个系统的内存使用情况,和所有对该应用程序做的内存限制
  • 睡眠:分析阻塞应用程序的资源
  • :识别锁和持有该锁的线程,确定线程持锁这么长时间的原因。原因可能是持锁线程阻塞在另一个锁上,这就需要进一步梳理。这件事情比较高阶,通常是熟悉应用程序和其锁机制的软件开发人员要做的事。

因为应用程序要等待工作,你常常会发现睡眠状态和锁状态的时间实际上就是空闲的时间。一个应用程序工作线程可能会为了工作等待条件变量(锁状态),或者是为了网络I/O(睡眠状态)。所以当看大量的睡眠和锁状态时间时,记住要深入检查是否真的空闲。

CPU剖析

剖析的目标是要判断应用程序是如何消耗CPU资源的。一个有效的技术是对CPU上的用户栈跟踪做采样并将采样结果联系起来。栈跟踪告诉我们所选择的代码路径,这是能够从高层和底层两方面揭示出应用程序消耗CPU的原因。

对栈的跟踪做采样会产生出上千行需要检查的输出,即使打印的仅仅是唯一栈的汇总输出,也是一样的。一个能快速了解这些数据的方法是用火焰图做可视化。

除了对栈跟踪做采样的方法外,还可以对当前运行的函数单独做采样。在某些情况下,这足以识别应用程序在使用CPU但所产生的输出却很少的原因,这让我们能更快发现和理解问题。

系统调用分析

基于系统调用的执行来研究下面这些状态:

  • 执行:CPU上(用户模式)
  • 系统调用:系统调用的时间(内核模式在允许或者等待)

系统调用的时间包括I/O、锁,以及其他系统调用类型。其他的线程状态,如可运行(等CPU)和匿名换页,都被简化了。即使碰到这些状态(CPU饱和或内存饱和),也能用USE方法在系统中识别出来。

研究执行状态可以用CPU剖析方法

研究系统调用(syscalls)有很多种方法。目标是要找出系统调用的世界花在了什么地方,还有系统调用的类型以及使用该系统调用的原因。

断点跟踪
传统的系统调用跟踪是设置系统调用入口和返回的断点。对于某些系统调用频繁的应用程序,这是一个激进的方法,因为它们的性能可能会变差一个数量级。

缓冲跟踪
有了缓冲跟踪,当目标程序在持续执行的时候,监测数据就可以缓冲在内核里。这是与断点跟踪的区别,断点跟踪会在每一个断点中断目标程序的执行。

I/O剖析

与CPU剖析的作用相似,I/O剖析判断的是I/O相关的系统调用执行的原因和方式。用DTrace可以做到这一点,检查用户栈里系统调用的跟踪栈。

利用栈跟踪能得到执行系统调用的原因。研究工作负载特征归纳法的其他属性也是很有用的:

  • Who:进程ID,用户名
  • What:I/O系统调用对象(例如,文件系统或套接字)、I/O尺寸、IOPS、吞吐量(B/s),以及其他属性
  • How:IOPS随时间的变化

工作负载特征归纳

应用程序向系统资源——CPU、内存、文件系统、磁盘和网络,施加负载,也通过系统调用向操作系统施加负载。

USE方法

USE方法检查使用率、饱和,以及所有硬件资源的错误。通过发现某一成为瓶颈的资源,许多应用程序的性能问题都能用该方法得到解决。

USE方法也适用于软件资源,取决于应用程序。如果你能找到应用程序的内部组件的功能图,对每种软件资源都做使用率、饱和和错误指标上的考量,看看有什么问题。

例如,有一个应用程序用一个工作线程来处理请求,请求在队列里排队等待被处理。把这个当作资源看待,那么三个指标可以做如下定义。

  • 使用率:在一定时间间隔内,忙于处理请求的线程平均数目。例如,50%意味着,平均下来,一半线程在忙于请求的工作
  • 饱和度:在一定时间间隔内,请求队列的平均长度。这显示出等待工作线程的有多少个请求
  • 错误:出于某种原因,请求被拒绝或失败

向下挖掘法

对于应用程序,向下挖掘法可以检查应用程序的服务操作作为开始然后向下至应用程序内部,看看它是如何执行的。对于I/O,向下挖掘的程度可以进入系统库、系统调用,甚至是内核。

锁分析

对于多线程的应用程序,锁可能会成为阻碍并行化和扩展性的瓶颈。锁的分析可以通过:

  • 检查竞争
  • 检查过长的持锁时间

第一个要识别的是当前是否有问题。过长的持锁时间并不一定会是问题,但是在将来随着更多的并行负载的加入,可能会产生问题的是试图识别每一个锁的名字(若存在)和通向使用锁的代码路径。

虽然有用于锁分析的专门工具,但有时用CPU剖析就可以解决问题。对于自旋锁来说,竞争出现的时候,CPU使用率也会变化,用栈跟踪的CPU剖析很容易就能识别出来。对于自适应 mutex锁,竞争的时候常常会有一些自旋,用栈跟踪的CPU剖析也能识别出来。不过对于这种情况,CPU剖析只能给出一部分信息,因为线程在等锁的时候可能已经被阻塞或在睡眠之中。

静态性能调优

静态性能调优的重点在于环境配置的问题。针对应用程序性能,检查静态配置如下各个方面:

  • 运行的应用程序是什么版本?有更新的版本吗》发布说明有提及性能提高吗?
  • 应用程序有哪些已知的性能问题?有提供搜索的bug数据库吗?
  • 应用程序如何配置?
  • 如果配置或调整的与默认值不同,是由于什么原因?(是基于测量和分析,还是猜想?)
  • 应用程序用到了对象缓存吗?缓存的大小是怎样的?
  • 应用程序是并发运行的吗?这是如何配置的(例如,线程池大小)?
  • 应用程序运行在特定模式下吗?(例如,调试模式启动会影响性能。)
  • 应用程序用到了哪些系统库?它们的版本是什么?
  • 应用程序用的是怎样的内存分配器?
  • 应用程序配置用大页面做堆吗?
  • 应用程序是编译的吗?编译器的版本是什么?编译器的选项和优化是哪些?是64位的吗?
  • 应用程序遇到错误了吗?错误之后会运行在降级模式吗?
  • 有没有系统设置的限制,以及对CPU、内存、文件系统、磁盘和网络使用的资源控制?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值