性能优化 - 案例篇:缓冲区

文章目录

  • Pre
  • 1. 引言
  • 2. 缓冲概念与类比
  • 3. Java I/O 中的缓冲实现
    • 3.1 FileReader vs BufferedReader:装饰者模式设计
    • 3.2 BufferedInputStream 源码剖析
      • 3.2.1 缓冲区大小的权衡与默认值
  • 4. 异步日志中的缓冲:Logback 异步日志原理与配置要点
    • 4.1 Logback 异步日志原理
    • 4.2 核心配置示例
      • 4.2.1 三个关键参数
  • 5. 缓冲区设计思路—同步 vs 异步
    • 5.1 同步缓冲
    • 5.2 异步缓冲
  • 6. Kafka 生产者缓冲示例
    • 6.1 Kafka 生产者缓冲原理
    • 6.2 缓冲区丢数据风险
    • 6.3 缓冲区过载与业务可用性
  • 7. 其他缓冲场景与示例
  • 8. 注意事项与异常场景
  • 9. 小结

在这里插入图片描述


Pre

性能优化 - 理论篇:常见指标及切入点

性能优化 - 理论篇:性能优化的七类技术手段

性能优化 - 理论篇:CPU、内存、I/O诊断手段

性能优化 - 工具篇:常用的性能测试工具

性能优化 - 工具篇:基准测试 JMH


  1. 引言:重温缓冲本质与设计动机;
  2. 缓冲概念与类比:蓄水池与生产线示例;
  3. Java I/O 中的缓冲实现:
    3.1 FileReader vs BufferedReader,装饰者模式设计;
    3.2 BufferedInputStream 源码剖析(fill() 逻辑与缓冲区扩容);
    3.3 缓冲区大小的权衡与默认值(8KB);
  4. 异步日志中的缓冲:Logback 异步日志原理与配置要点(queueSize、maxFlushTime、discardingThreshold);
  5. 缓冲区设计思路—同步 vs 异步:
    5.1 同步缓冲:单线程或方法内批量触发策略;
    5.2 异步缓冲:生产者策略(抛弃/阻塞/异常)与多线程消费者的同步问题;
  6. Kafka 生产者缓冲示例:batch.size、linger.ms 如何影响消息丢失与可用性;
  7. 其他缓冲场景与示例:StringBuilder/StringBuffer、操作系统网络缓冲、数据库 Buffer Pool、ID 生成器缓存;
  8. 注意事项与异常场景:缓冲区数据丢失风险、预写日志与 WAL 简述;
  9. 小结:缓冲区优化的收益与权衡;

1. 引言

在 性能优化 - 理论篇:性能优化的七类技术手段 中,已经初步了解“复用优化”领域下的两大子方向:缓存(Cache)缓冲(Buffer)

接下来我们聚焦于“缓冲”这一个技术手段,深入理解它在 Java 语言与中间件中的各类应用场景,以及在设计时需要注意的权衡与异常处理。


为什么要用缓冲?

  • 设备之间速度差异:CPU/内存读写速度≪磁盘或网络 I/O;
  • 频繁、小量的随机 I/O 会导致寻道或上下文切换开销巨大;
  • 缓冲通过在内存中聚合数据,批量顺序写/读,显著提高吞吐。

接下来,将从概念、源码、配置与设计思路几个维度展开,对“缓冲”有一个系统化的认识。


2. 缓冲概念与类比

缓冲(Buffer)最本质的作用,是将“生产方”与“消费方”之间的不一致速度,化解为一个容量有限的“中间池”。

  • 蓄水池比喻

    • 放水端(消费方):以恒定速率流出,就如程序中读取缓冲区后进行处理;
    • 进水端(生产方):速率不确定,可能快也可能慢,就如磁盘或网络向缓冲写数据;
    • 缓冲区(蓄水池)大小:当进水过快或消费端处理慢时,水就会在池中积累;当池满时,生产方必须等待或做其他处理。
  • 包饺子流水线

    • 擀皮工序 vs 包馅工序,如果一擀一交彼此就停止,效率低;
    • 加入一个盆子作为中间缓冲,擀皮不断往盆里扔,包馅者随时取用,两者最大限度保持各自节奏。

从宏观而言,Java 的堆本身也可视作一个“对象缓冲区”——应用线程在其中不断分配对象,而垃圾回收线程(GC)则以另一种节奏“消费”这些对象。

对比于“缓存(Cache)”,缓冲侧重于写(或读)过程中的批量与顺序,让慢速设备前端获得“可持续的小幅流量”。


3. Java I/O 中的缓冲实现

在 Java 中,缓冲最常见的应用场景就是文件与网络 I/O。底层设备(如磁盘、Socket)本身速度较慢,而 Java I/O 通过装饰者模式,将原始流包装为“带缓冲的流”,让单次 read()/write() 调用变为“先读/写到内存缓冲区,再批量交给底层设备”。

3.1 FileReader vs BufferedReader:装饰者模式设计

在这里插入图片描述

  • FileReader:直接从磁盘逐个字符读取,一次 read() 需要:

    1. 操作系统触发文件系统寻道,读取一个字节到内核缓冲;
    2. 再从内核缓冲拷贝到用户空间;
    3. 返回给应用。

    由于每调用一次 read() 都要重复上述步骤,效率极低。

  • BufferedReader:以 Reader reader = new BufferedReader(new FileReader(path)) 方式包装后:

    1. 在首次 read() 时,会一次性将后续 buffer.length 个字节(默认 8KB)读入 Java 堆内存中的 byte[] buffer;
    2. 之后的多次调用 read(),只需从内存 buffer 中读取字符,直到 pos >= count,才触发下次 fill()
    3. 大多数情况下,减少了对磁盘和内核空间的多次交互。

    这种添加功能而不修改原类代码的模式,就是装饰者(Decorator)模式。

    public class Demo {public int readWithoutBuffer(String path) throws IOException {int result = 0;try (Reader reader = new FileReader(path)) {int value;while ((value = reader.read()) != -1) {result += value;}}return result;}public int readWithBuffer(String path) throws IOException {int result = 0;try (Reader reader = new BufferedReader(new FileReader(path))) {int value;while ((value = reader.read()) != -1) {result += value;}}return result;}
    }
    
    • 如果将两段代码用 JMH 对比测试,后者在绝大多数文件大小与硬件环境下,都能以数倍乃至十数倍的速度胜出(未考虑 OS page cache)。

3.2 BufferedInputStream 源码剖析

BufferedInputStream 为例,下面我们重点关注它的 read()fill() 实现,理解缓冲区如何管理数据。

public synchronized int read() throws IOException {if (pos >= count) {fill();if (pos >= count)return -1;}return getBufIfOpen()[pos++] & 0xff;
}private void fill() throws IOException {byte[] buffer = getBufIfOpen();if (markpos < 0)pos = 0;            /* no mark: throw away the buffer */else if (pos >= buffer.length)  /* no room left in buffer */if (markpos > 0) {  /* can throw away early part of buffer */int sz = pos - markpos;System.arraycopy(buffer, markpos, buffer, 0, sz);pos = sz;markpos = 0;} else if (buffer.length >= marklimit) {markpos = -1;   /* buffer got too big, invalidate mark */pos = 0;        /* drop buffer contents */} else if (buffer.length >= MAX_BUFFER_SIZE) {throw new OutOfMemoryError("Required array size too large");} else {            /* grow buffer */int nsz = (pos <= MAX_BUFFER_SIZE - pos)? pos * 2 : MAX_BUFFER_SIZE;if (nsz > marklimit)nsz = marklimit;byte nbuf[] = new byte[nsz];System.arraycopy(buffer, 0, nbuf, 0, pos);buf = nbuf;buffer = nbuf;}count = pos;int n = getInIfOpen().read(buffer, pos, buffer.length - pos);if (n > 0)count = n + pos;
}
  1. poscount

    • pos:当前缓冲区已经消费到的位置索引;
    • count:缓冲区中实际可用字节数(读取自底层 InputStream)。
  2. pos >= count 时,调用 fill()

    • 无 mark 逻辑:如果未在流上调用过 mark(),则直接 pos = 0,丢弃旧缓冲区内容;
    • 有 mark 逻辑:如果调用过 mark() 并且 buffer 尚未超过 marklimit,会先将 buffer[markpos, pos) 部分拷贝到 buffer[0, sz),保留标记区域;否则当 buffer 大小接近 marklimit,会放弃缓存并重置 markpos = -1
    • 缓冲区扩容:若 buffer.length < marklimitpos >= buffer.length,则按 pos*2marklimit 的大小扩容,以承载更多数据(最大不超过 MAX_BUFFER_SIZE)。
  3. 从底层流读取数据

    • 调用 getInIfOpen().read(buffer, pos, buffer.length - pos),一次性将尽量多的数据填入 buffer[pos, buffer.length)
    • 读取后把 count = pos + n,表示新的缓冲区可读取字节数。
    • 随后 read() 方法从内存 buffer 中依次提供单字节给调用者。

这样,绝大多数 reader.read() 调用都不会触发一次真正的磁盘或网络 I/O,而是走内存读取,直到缓冲耗尽才会调用一次底层的 read(...)


3.2.1 缓冲区大小的权衡与默认值

  • 为什么不一次性把整个文件都读到缓冲?

    • 缓冲区太小时,需要频繁地调用 fill(),失去缓冲效果;
    • 缓冲区太大,单次 fill() 就涉及大量内存分配并可能导致垃圾回收压力;
    • 默认缓冲区大小一般为 8192 字节(8KB),是工业界常见的折中值,既能减少单次 fill() 带来的系统调用开销,又不会占用过多堆内存。
  • 调整缓冲区大小的思路

    • 对于小文件或小 HTTP 响应,可将缓冲区设置为 4KB 或更小,减少内存消耗;
    • 对于大文件复制、视频流处理等场景,可适当增大为 32KB 或 64KB,以减少 I/O 调用频率;
    • 但文件过大时,缓存整个文件到内存会导致 OOM,因此需结合实际业务与可用堆内存大小,谨慎配置。

4. 异步日志中的缓冲:Logback 异步日志原理与配置要点

在高并发服务中,同步日志会成为性能瓶颈:

  • 每次调用 logger.info(...) 都要先拼接日志消息,再调用底层 I/O 将文本写入磁盘;
  • 如果并发量大,业务线程在等待磁盘 I/O 完成时被阻塞,整体延迟显著增加;
  • 即便日志输出到控制台,也会占用 CPU 时间来格式化。这时可以引入“异步日志”来解耦业务线程与磁盘写入。

4.1 Logback 异步日志原理

Logback 的异步日志基于 AsyncAppender,其核心思路:

  1. 业务线程(生产者):调用 logger 时,只需将待写入的日志事件先放入内存中的阻塞队列(ArrayBlockingQueue);
  2. 异步 Worker 线程(消费者):在后台单线程循环不断地从队列 poll() 日志事件,然后批量地将它们写入磁盘。

这样,业务线程仅关心向缓冲队列“放入”日志,I/O 写入由独立线程异步完成。

在这里插入图片描述

4.2 核心配置示例

logback.xml 中添加如下内容:

<!-- 定义一个 FileAppender,负责将日志写入磁盘 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender"><file>app.log</file><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder>
</appender><!-- 定义一个 AsyncAppender,将日志事件缓冲到队列 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"><!-- 当队列中的事件数量达到 discardingThreshold 时,可以丢弃低级别日志 --><discardingThreshold>0</discardingThreshold><!-- 队列大小:最多容纳 512 条事件 --><queueSize>512</queueSize><!-- 指定要异步包装的目标 appender --><appender-ref ref="FILE"/>
</appender><root level="INFO"><!-- 使用异步日志器 --><appender-ref ref="ASYNC"/>
</root>

4.2.1 三个关键参数

  1. queueSize(队列容量)

    • 默认值为 256;当待写入事件数量超过 queueSize 时,生产者线程会根据 discardingThreshold 策略进行处理;
    • 如果设置过大,进程突然断电,队列内未落盘的日志全部丢失风险增大;
  2. discardingThreshold(丢弃阈值)

    • 取值范围 0 <= discardingThreshold <= queueSize
    • 当队列长度 ≥ discardingThreshold 时,低于或等于设定级别的日志事件可被丢弃,以保护高优先级日志;
    • 默认值为 queueSize × 0.8,即队列达到 80% 时开始丢弃低级别日志;若设置为 0,则不丢弃任何事件,但可能导致生产者阻塞或抛出异常;
  3. maxFlushTime(关闭时最长等待时间)

    • 当应用优雅关闭时,AsyncAppender 会调用 worker.join(maxFlushTime),等待后台线程将剩余日志写完;
    • 如果等待超时,应用仍会强制退出,此时缓冲区中未落盘的日志将丢失;
    • 合理设置该值(如 5 秒或 10 秒),在性能与丢失风险之间权衡。

5. 缓冲区设计思路—同步 vs 异步

缓冲区优化常见于需要对“生产端”和“消费端”解耦的场景。但在设计时,需要考虑“同步缓冲”与“异步缓冲”两种模式,取舍点在于编程模型复杂度与性能收益。

在这里插入图片描述

5.1 同步缓冲

  • 模型示意

    生产者 →→ [缓冲区] →→ 消费者
    

在这里插入图片描述

整个过程在同一个线程或调用链中执行。常见策略:

  1. 阈值触发写入:当缓冲区积累元素数量 ≥ flushThreshold,或累积字节数 ≥ byteThreshold 时,一次性批量写入底层资源;
  2. 定时触发写入:如果缓冲区在 maxIdleTime 内未达到 flushThreshold,则定时将已有数据写出,保证高延迟数据得以及时消费;
  • 优缺点

    • 优点:编程模型直观、逻辑简单,无额外线程;
    • 缺点:当底层写入耗时出现波动(如突发磁盘抖动),会阻塞生产者线程,导致整体响应能力下降。
  • 示例

    • 字符串拼接时使用 StringBuilder,一次性将多次 append() 的结果通过 toString() 写入磁盘;
    • JDBC 批量插入:在内存中将多条 SQL 语句缓存在 PreparedStatement 中,达到 batchSize 时通过 executeBatch() 一次性写入数据库。

5.2 异步缓冲

  • 模型示意

    生产者(线程 A) →→ [缓冲区(队列)] →→ 消费者(后台线程 B)
    

在这里插入图片描述

生产者仅负责往缓冲区写入;消费者由独立线程不断从缓冲区读取并写出。

  • 生产者策略

    1. 阻塞:当队列已满,生产者调用 put() 时会阻塞,直到有空位;
    2. 非阻塞失败:调用 offer(),若缓冲区已满则返回 false,让上层逻辑根据返回值做重试或丢弃;
    3. 异常抛出:直接在 put() 或自定义方法中抛出 RejectedExecutionException 等,通知调用方“缓冲已满”;
    4. 回调机制:在生产者线程注册回调,当数据真正被消费后才触发,也可以用于跟踪已消费数据。
  • 消费者设计

    • 启动单线程或线程池,不断从队列 poll() 数据;
    • 处理逻辑应考虑批量拉取,以减少 I/O 写入次数,例如 drainTo()
    • 若消费者处理速度不足,队列会持续积压;生产者需根据业务侧容忍值判断是否阻塞或快速失败。
  • 多消费者与同步问题

    • 当缓冲区被多个消费者并发读写时,需要保证数据顺序或一致性;
    • 可使用有序队列(如基于 BlockingQueue)或根据分区(Partition)分流;
    • 同时考虑多个消费者线程各自的吞吐能力与负载均衡。

6. Kafka 生产者缓冲示例

Kafka 生产者客户端的“批量发送”与“缓冲区”设计。下面以常见参数 batch.sizelinger.ms 为例,说明缓冲区对性能与可靠性的影响。

在这里插入图片描述

6.1 Kafka 生产者缓冲原理

  • batch.size(字节):指定针对每个 partition 为单个批次消息设置的最大字节容量,默认为 16KB。当该容量被填满后,生产者立即将该 batch 发送给对应的 broker。
  • linger.ms(毫秒):指定“批次最大等待时间”,即在 batch.size 未被填满的情况下,生产者也会等待 linger.ms 后把已积累的消息强制发送,以减少每条消息都立刻网络发送带来的开销。

具体流程:

  1. 当调用 producer.send(record) 时,Kafka 生产者客户端先把 record 序列化后放入对应 partition 的缓冲队列(内存);
  2. 如果当前 batch 的累积字节数 ≥ batch.size,则立即触发发送;
  3. batch.size 未满,且当前时间超过 linger.ms,也会把已累积消息发送;
  4. 发送出发后会异步等待 broker ACK 确认或重试。

6.2 缓冲区丢数据风险

假设生产者程序发生以下情况:

  • 机器突然断电
  • JVM 进程被 kill -9 杀死

此时如果缓冲队列中仍有尚未被 send() 到 Broker 或未获 ACK 的消息,全部会丢失。默认情况下,缓冲区大小为 16KB,如果生产方业务持续产生大量消息,且 broker 短暂不可用,就会导致缓冲区快速填满。

解决方案:

  1. 缩小 batch.size:使得缓冲队列更频繁地发送,虽然牺牲吞吐量,但可减少丢失概率;
  2. 降低 linger.ms:即使在低流量情况下,也会在较短等待后发送,保证消息尽快到达 broker;
  3. 开启幂等性与 ACK 配置:设置 enable.idempotence=true 并且 acks=all,让 broker 等待所有 ISR(in-sync replicas)确认后再 ACK,确保至少写入一个副本或多副本;
  4. 生产者写入前落盘预写日志:在生产者本地先记录“即将发送的消息”,待发送成功后再删除,重启后可根据预写日志补发;
  5. 使用电池或 UPS:在极端断电场景下保证机器有足够时间将缓冲持久化到磁盘。

6.3 缓冲区过载与业务可用性

当 broker 暂时不可用或网络抖动时,生产者缓冲区会不断积累消息,直至达到 buffer.memory(内存缓冲池)或达成 max.block.ms,此时默认行为是阻塞调用线程。若业务对等待较敏感,可能导致线程被长时间阻塞,最终耗尽线程池资源,从而引起整个服务不可用。

可选对策:

  • 调整 max.block.ms:一旦超时则抛出 TimeoutException 告知调用方“消息积压,发送超时”;
  • 限流策略:在业务层做流控(如令牌桶),防止短时间内压入大量消息;
  • 异步重试:将消息暂存在本地队列或数据库,后台线程定时重试投递;

7. 其他缓冲场景与示例

除了文件 I/O 与消息中间件,缓冲思想在常见的 Java 开发中无处不在:

  1. StringBuilder / StringBuffer

    • 在 Java 中,频繁使用 String 做拼接会产生大量临时对象;
    • StringBuilder 通过在内存中维护一个 char[] 缓冲,每次 append() 写入缓冲中,最后一次性 toString() 时整体创建一个 String,显著提高拼接性能。
  2. 操作系统网络缓冲(SO_SNDBUF / SO_RCVBUF)

    • TCP Socket 在内核中会分配发送与接收缓冲区大小,可通过 socket.setSendBufferSize(...) / socket.setReceiveBufferSize(...) 进行调优;
    • 适当增大操作系统缓冲区,可应对应用端瞬时突发流量,减少 packet drop;但过大会占用更多内核内存。
  3. 数据库 InnoDB Buffer Pool

    • MySQL InnoDB 存储引擎通过 innodb_buffer_pool_size 配置,将数据页和索引页缓存在内存中,减少磁盘读取;
    • 合理将 buffer_pool_size 设置为物理内存的 60%~80%,能显著提升查询和写入性能。
  4. ID 生成器缓存

    • 常见的全局递增 ID 生成,如 Twitter Snowflake 或数据库自增 ID,为了减少每次网络交互,往往将一段 ID(如 1000 个)一次性从数据库或中央服务拉取到本地缓冲;
    • 应用只需从本地缓冲取得下一个 ID,当耗尽后再异步从中央服务拉取下一段,减少延迟。

8. 注意事项与异常场景

虽然缓冲区能带来明显性能提升,但在设计时还要考虑以下几方面的风险与权衡:

  1. 缓冲区数据丢失

    • 突发断电、kill -9 等强制销毁进程时,缓冲区中的数据尚未落盘或未发送,全部丢失;
    • 对金融、订单类等对数据可靠性要求极高的系统,可通过“先写 WAL 日志再入缓冲”来保证数据不丢;
  2. 缓冲区内存占用与 OOM

    • 大容量缓冲区占用堆内存较大,可能导致垃圾回收压力增大或堆内存不足;
    • 需根据可用物理内存与业务吞吐量合理设置,如 8KB64KB、512MB2GB 等;
  3. 顺序与一致性问题

    • 在多线程/多消费者场景下,若需要保证“消息顺序”,必须使用单队列或分区队列;
    • 对于都依赖同一缓存状态的读写,需要在并发消费者之间做好状态一致性或加锁、防重入等处理;
  4. 性能收益递减

    • 缓冲区过小则频繁交互;过大则内存占用过高;需要在吞吐与延迟之间做折中;
    • 在高并发网络场景,Socket / OS 层面也会加入多级缓冲,需要一体化考虑。
  5. 预写日志与数据恢复

    • 常见做法是在写入缓冲前,先将关键元数据(例如 Kafka 消息 key)写到本地磁盘日志;待缓冲数据真正被提交到 broker 后,再标记该日志为成功。重启后扫描日志,补发未成功消息。
    • 这种“先 WAL 后缓存”的策略会带来额外写盘开销,需要衡量业务对丢失率的容忍度。

9. 小结

系统地探讨了**缓冲(Buffer)**在 Java 语言与中间件中的典型应用:

  1. 缓冲本质与类比

    • 缓冲区是解耦生产者与消费者速度差异的内存“蓄水池”;
    • 让慢速设备接收“小而频繁” → “大而顺序” 的 I/O 请求,大幅提高吞吐。
  2. Java I/O 缓冲实现

    • BufferedReader / BufferedInputStream 通过 fill() 方法一次性从底层流读入 8KB(默认)数据到内存;
    • 通过 poscount 指针管理缓冲区消费位置;自动扩容至 marklimit,在保证性能的同时兼容 mark()/reset()
  3. 异步日志缓冲

    • Logback AsyncAppender 将日志事件先缓存在 ArrayBlockingQueue 中,后台线程异步写入磁盘;
    • 重要参数:queueSize(队列容量)、discardingThreshold(丢弃阈值)、maxFlushTime(关闭时等待时间)。
  4. 缓冲设计思路—同步 vs 异步

    • 同步缓冲:批量触发策略简单,但会阻塞生产者;
    • 异步缓冲:解耦写入与消费,需设计生产者满载后的处理策略与多消费者同步。
  5. Kafka 生产者缓冲示例

    • 通过 batch.size + linger.ms 实现消息批量发送;
    • 缓冲区满或超时触发网络发送,带来吞吐与延迟权衡;
    • 在断电或 kill -9 场景下,缓冲中消息或未获 ACK 消息会丢失,可结合写盘预写日志或副本策略降低风险。
  6. 其他缓冲场景

    • StringBuilderStringBuffer 字符串拼接;
    • 操作系统 Socket 缓冲(SO_SNDBUF/SO_RCVBUF);
    • 数据库 InnoDB Buffer Pool;
    • ID 生成器段缓存等。
  7. 注意事项与异常场景

    • 缓冲区丢失风险、内存占用风险;
    • 顺序一致性与并发读写冲突;
    • 性能收益递减点;
    • WAL/预写日志策略平衡可靠性与性能。

总之,缓冲区优化是 Java 性能优化中一项非常重要的技术手段,它既能显著提高磁盘与网络 I/O 吞吐,也带来了异步设计下的编程复杂度与故障恢复挑战。

在实际项目中,应结合业务对“数据丢失概率”与“延迟/吞吐”之间的容忍度,合理设置缓冲大小与处理策略。


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/907920.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文档整合自动化

主要功能是按照JSON文件&#xff08;Sort.json&#xff09;中指定的顺序合并多个Word文档&#xff08;.docx&#xff09;&#xff0c;并清除文档中的所有超链接。最终输出合并后的文档名为"sorted_按章节顺序.docx"。 主要分为几个部分&#xff1a; 初始化配置 定…

嵌入式(C语言篇)Day13

嵌入式Day13 一段话总结 文档主要介绍带有头指针和尾指针的单链表的实现及操作&#xff0c;涵盖创建、销毁、头插、尾插、按索引/数据增删查、遍历等核心操作&#xff0c;强调头插/尾插时间复杂度为O(1)&#xff0c;按索引/数据操作需遍历链表、时间复杂度为O(n)&#xff0c;并…

【ASR】基于分块非自回归模型的流式端到端语音识别

论文地址:https://arxiv.org/abs/2107.09428 摘要 非自回归 (NAR) 模型在语音处理中越来越受到关注。 凭借最新的基于注意力的自动语音识别 (ASR) 结构,与自回归 (AR) 模型相比,NAR 可以在仅精度略有下降的情况下实现有前景的实时因子 (RTF) 提升。 然而,识别推理需要等待…

RNN循环网络:给AI装上“记忆“(superior哥AI系列第5期)

&#x1f504; RNN循环网络&#xff1a;给AI装上"记忆"&#xff08;superior哥AI系列第5期&#xff09; 嘿&#xff01;小伙伴们&#xff0c;又见面啦&#xff01;&#x1f44b; 上期我们学会了让AI"看懂"图片&#xff0c;今天要给AI装上一个更酷的技能——…

DAY41 CNN

可以看到即使在深度神经网络情况下&#xff0c;准确率仍旧较差&#xff0c;这是因为特征没有被有效提取----真正重要的是特征的提取和加工过程。MLP把所有的像素全部展平了&#xff08;这是全局的信息&#xff09;&#xff0c;无法布置到局部的信息&#xff0c;所以引入了卷积神…

【仿生系统】爱丽丝机器人的设想(可行性优先级较高)

非程序化、能够根据环境和交互动态产生情感和思想&#xff0c;并以微妙、高级的方式表达出来的能力 我们不想要一个“假”的智能&#xff0c;一个仅仅通过if-else逻辑或者简单prompt来模拟情感的机器人。您追求的是一种更深层次的、能够学习、成长&#xff0c;并形成独特“个性…

面向连接的运输:TCP

目录 TCP连接 TCP报文段结构 往返时间估计与超时 可靠数据传输 回退N步or超时重传 超时间隔加倍 快速重传 流量控制 TCP连接管理 三次握手 1. 客户端 → 服务器&#xff1a;SYN 包 2. 服务器 → 客户端&#xff1a;SYNACK 包 3. 客户端 → 服务器&#xff1a;AC…

SpringAI系列 - 升级1.0.0

目录 一、调整pom二、MessageChatMemoryAdvisor调整三、ChatMemory get方法删除lastN参数四、QuestionAnswerAdvisor调整Spring AI发布1.0.0正式版了😅 ,搞起… 一、调整pom <properties><java.version>17</java.version><spring-ai.version>

前端高频面试题2:JavaScript/TypeScript

1.什么是类数组对象 一个拥有 length 属性和若干索引属性的对象就可以被称为类数组对象&#xff0c;类数组对象和数组类似&#xff0c;但是不能调用数组的方法。常见的类数组对象有 arguments 和 DOM 方法的返回结果&#xff0c;还有一个函数也可以被看作是类数组对象&#xff…

Spring Security入门:创建第一个安全REST端点项目

项目初始化与基础配置 创建基础Spring Boot项目 我们首先创建一个名为ssia-ch2-ex1的空项目(该名称与配套源码中的示例项目保持一致)。项目需要添加以下两个核心依赖: org.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-secur…

秋招Day12 - 计算机网络 - UDP

说说TCP和UDP的区别&#xff1f; TCP使用无边界的字节流传输&#xff0c;可能发生拆包和粘包&#xff0c;接收方并不知道数据边界&#xff1b;UDP采用数据报传输&#xff0c;数据报之间相互独立&#xff0c;有边界。 应用场景方面&#xff0c;TCP适合对数据的可靠性要求高于速…

【QQ音乐】sign签名| data参数加密 | AES-GCM加密 | webpack (下)

1.目标 网址&#xff1a;https://y.qq.com/n/ryqq/toplist/26 我们知道了 sign P(n.data)&#xff0c;其中n.data是明文的请求参数 2.webpack生成data加密参数 那么 L(n.data)就是密文的请求参数。返回一个Promise {<pending>}&#xff0c;所以L(n.data) 是一个异步函数…

Codeforces Round 1028 (Div. 2)(A-D)

题面链接&#xff1a;Dashboard - Codeforces Round 1028 (Div. 2) - Codeforces A. Gellyfish and Tricolor Pansy 思路 要知道骑士如果没了那么这个人就失去了攻击手段&#xff0c;贪心的来说我们只需要攻击血量少的即可&#xff0c;那么取min比较一下即可 代码 void so…

【存储基础】存储设备和服务器的关系和区别

文章目录 1. 存储设备和服务器的区别2. 客户端访问数据路径场景1&#xff1a;经过服务器处理场景2&#xff1a;客户端直连 3. 服务器作为"中转站"的作用 刚开始接触存储的时候&#xff0c;以为数据都是存放在服务器上的&#xff0c;服务器和存储设备是一个东西&#…

macOS 安装 Grafana + Prometheus + Node Exporter

macOS 安装指南&#xff1a;Grafana Prometheus Node Exporter 目录简介&#x1f680; 快速开始 安装 Homebrew1. 安装 Homebrew2. 更新 Homebrew 安装 Node Exporter使用 Homebrew 安装验证 Node Exporter 安装 Prometheus使用 Homebrew 安装验证安装 安装 Grafana使用 Home…

不可变集合类型转换异常

记录一个异常&#xff1a;class java.util.ImmutableCollections$ListN cannot be cast to class java.util.ArrayList (java.util.ImmutableCollections$ListN and java.util.ArrayList 文章目录 1、原因2、解决方式一3、解决方式二4、关于不可变集合的补充4.1 JDK8和9的对比4…

【DAY37】早停策略和模型权重的保存

内容来自浙大疏锦行python打卡训练营 浙大疏锦行 知识点&#xff1a; 过拟合的判断&#xff1a;测试集和训练集同步打印指标模型的保存和加载 仅保存权重保存权重和模型保存全部信息checkpoint&#xff0c;还包含训练状态 早停策略 作业&#xff1a; 对信贷数据集训练后保存权…

【Zephyr 系列 3】多线程与调度机制:让你的 MCU 同时干多件事

好的,下面是Zephyr 系列第 3 篇:聚焦 多线程与调度机制的实践应用,继续面向你这样的 Ubuntu + 真板实战开发者,代码清晰、讲解通俗、结构规范,符合 CSDN 高质量博客标准。 🧠关键词:Zephyr、线程调度、k_thread、k_sleep、RTOS、BluePill 📌适合人群:想从裸机开发进…

实现RabbitMQ多节点集群搭建

目录 引言 一、环境准备 二、利用虚拟机搭建 ​ 三、镜像集群配置 四、HAProxy实现负载均衡(主用虚拟机操作) 五、测试RabbitMQ集群搭建情况 引言 在现代分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff09;扮演着至关重要的角色,而 RabbitMQ 作为…

异步上传石墨文件进度条前端展示记录(采用Redis中String数据结构实现-苏东坡版本)

昔者&#xff0c;有客临门&#xff0c;亟需自石墨文库中撷取卷帙若干。此等文册&#xff0c;非止一卷&#xff0c;乃累牍连篇&#xff0c;亟需批量转置。然吾辈虑及用户体验&#xff0c;当效东坡"腹有诗书气自华"之雅意&#xff0c;使操作如行云流水&#xff0c;遂定…