=========
优化策略的第二大类是“预先和延后处理”,这一类别也有两个互相对立的策略。一个是预先或者提前处理,另外一个是延后或者惰性处理。
3. 预先 / 提前处理
预先 / 提前处理策略同样也表现在很多领域,比如网站页面资源的提前加载。Web 标准规定了至少两种提前加载的方式:preload 和 prefetch,分别用不同的优先级来加载资源,可以显著地提升页面下载性能。
很多文件系统有预读的功能,就是提前从磁盘读取额外的数据,为下次上层应用程序读数据做准备。这个功能对顺序读取非常有效,可以明显地减少磁盘请求的数量,从而提升读数据的性能。
CPU 和内存也有相应的预取操作,就是将内存中的指令和数据,提前存放到缓存中,从而加快处理器执行速度。缓存预取可以通过硬件或者软件实现,也就是分为硬件预取和软件预取两类。
硬件预取是通过处理器中的硬件来实现的。该硬件会一直监控正在执行程序中请求的指令或数据,并且根据既定规则,识别下一个程序需要的数据或指令并预取。
软件预取是在程序编译的过程中,主动插入预取指令(prefetech),这个预取指令可以是编译器自己加的,也可以是我们加的代码。这样在执行过程中,在指定位置就会进行预取的操作。
4. 延后 / 惰性处理
延后 / 惰性处理策略和前面说的预先 / 提前处理正好相反。就是尽量将操作(比如计算),推迟到必需执行的时刻,这样很可能避免多余的操作,甚至根本不用操作。
运用这一策略最有名的例子,就是 COW(Copy On Write,写时复制)。假设多个线程都想操作一份数据,一般情况下,每个线程可以自己拷贝一份,放到自己的空间里面。但是拷贝的操作很费时间。系统如果采用惰性处理,就会将拷贝的操作推迟。如果多个线程对这份数据只有读的请求,那么同一个数据资源是可以共享的,因为“读”的操作不会改变这份数据。当某个线程需要修改这一数据时(写操作),系统就将资源拷贝一份给该线程使用,允许改写,这样就不会影响别的线程。
COW 最广为人知的应用场景有两个。一个是 Unix 系统 fork 调用产生的子进程共享父进程的地址空间,只有到某个子进程需要进行写操作才会拷贝一份。另一个是高级语言的类和
容器,比如 Java 中的 CopyOnWrite 容器,用于多线程并发情况下的高效访问。C++ 里面经常使用的 STL 标准模板库中的很多类,比如 string 类,也是具有写时才拷贝技术的类。
三、并行 / 异步操作
===========
优化策略的第三大类是“并行 / 异步操作”。并行和异步两种操作虽然看起来很不一样,其实有异曲同工之妙,就是都把一条流水线和处理过程分成了几条,不管是物理上分还是逻辑上分。
5. 并行操作
并行操作是一种物理上把一条流水线分成好几条的策略。直观上说,一个人干不完的活,那就多找几个人来干。并行操作既增加了系统的吞吐量,又减少了用户的平均等待时间。比如现代的 CPU 都有很多核,每个核上都可以独立地运行线程,这就是并行操作。
并行操作需要我们的程序有扩展性,不能扩展的程序,就无法进行并行处理。这里的并行概念有不同的粒度,比如是在服务器的粒度(所谓的横向扩展),还是在多线程的粒度,甚至是在指令级别的粒度。
绝大多数互联网服务器,要么使用多进程,要么使用多线程来处理用户的请求,以充分利用多核 CPU。另外一种情况就是在有 IO 阻塞的地方,也是非常适合使用多线程并行操作的,因为这种情况 CPU 基本上是空闲状态,多线程可以让 CPU 多干点活。
6. 异步操作
异步操作这一策略和并行操作不同,这是一种逻辑上把一条流水线分成几条的策略。
我们首先在编程的领域澄清一下概念:同步和异步。同步和异步的区别在于一个函数调用之后,是否直接返回结果。如果函数挂起,直到获得结果才返回,这是同步;如果函数马上返回,等数据到达再通知函数,那么这就是异步。
我们知道 Unix 下的文件操作,是有 block 和 non-block 的方式的,有些系统调用也是block 式的,如:Socket 下的 select 等。如果我们的程序一直是同步操作,那么就会非常影响性能。采用异步操作的话,虽然稍微增加一点程序的复杂度,但会让性能的吞吐率有很大提升。
现代的语言往往对异步操作有比较好的支持,使得异步编程变得更加简单,可读性也更好。
四、缓存 / 批量合并
===========
“缓存 / 批量合并”是优化策略中的第四大类。缓存和批量合并这两个策略,有些场景下会同时起作用,所以我把它们放在一起。
7. 缓存数据
缓存的本质是加速访问。这是一个用得非常普遍的策略,几乎体现在计算机系统里面每一个模块和领域,CPU、内存、文件系统、存储系统、内容分布、数据库等等,都会遵循这样的策略。
我们最熟悉的应该就是 CPU 的各级缓存了。在文件系统、存储系统和数据库系统里面,也有快速缓存来存储经常访问的数据,目的是尽量提高缓存命中率,从而避免访问比较慢的存储介质。
对于一个基于 Web 的应用服务,前端会有浏览器缓存,有 CDN 存放在边缘服务器上,有反向代理提供的静态内容缓存;后端则还会有服务器本地缓存。
程序设计中,对于可能重复创建和销毁,且创建销毁代价很大的对象(比如套接字和线程),也可以缓存,对应的缓存形式,就是连接池和线程池等。
对于消耗较大的计算,也可以将计算结果缓存起来,下次可以直接读取结果。比如对递归代码的一个有效优化手段,就是缓存中间结果。
8. 批量合并处理
在有 IO(比如网络 IO 和磁盘 IO)的时候,合并操作和批量操作往往能提升吞吐量,提高性能。
我们最常见的是批量 IO 读写。就是在有多次 IO 的时候,可以把它们合并成一次读写数据。这样可以减少读写时间和协议负担。比如,GFS 写文件的时候,尽量批量写,以减少IO 开销。
对数据库的读写操作,也可以尽量合并。比如,对键值数据库的查询,最好一次查询多个键,而不要分成多次。
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
总结
在清楚了各个大厂的面试重点之后,就能很好的提高你刷题以及面试准备的效率,接下来小编也为大家准备了最新的互联网大厂资料。
[外链图片转存中…(img-CvmJJfUV-1711109609047)]
[外链图片转存中…(img-ihAhskWT-1711109609047)]
[外链图片转存中…(img-UdBr0p4M-1711109609048)]
[外链图片转存中…(img-LZbhlmOS-1711109609048)]