当前位置: 首页 > news >正文

大模型最新面试题系列:模型部署(二)

一、如何通过unsloth的批处理(Batching)功能提升推理效率?

Unsloth的批处理(Batching)功能通过整合硬件加速、内存优化和动态调度技术,显著提升大模型推理效率。以下是具体实现方法和实际应用场景的深度解析:

动态批处理与硬件协同优化

Unsloth的核心机制是将多个推理请求合并为一个批次,利用GPU的并行计算能力提升吞吐量。例如,当处理多个用户的实时查询时,系统会自动将请求打包成批次(如16个请求/批次),通过矩阵运算的批量处理减少单次计算的延迟。这种方式尤其适用于Transformer架构的模型,因为其自注意力机制在大批次下的计算效率更高。

硬件加速策略
  • 混合精度计算:默认启用BF16/FP16混合精度,在保持精度的同时减少显存带宽占用。例如,Llama-3.1(8B)在A100 40GB GPU上使用BF16时,吞吐量提升2倍,显存占用降低50%。
  • CUDA内核优化:通过Triton语言重写关键算子(如注意力模块),将计算密集型操作的执行效率提升30%-50%。例如,Mistral-7B模型在RTX 4090上的推理速度可达每秒4000 tokens。
动态批次调整
  • 自适应批次大小:根据输入请求的到达率自动调整批次大小。例如,在高并发时将批次大小从8增加到32,提升吞吐量;在低并发时降至4,减少延迟。
  • 长上下文处理:通过RoPE扩展技术支持超长上下文(如Llama-3.3 70B支持89K上下文),在处理长文本生成任务时保持批次效率。

内存优化与显存管理

Unsloth通过量化技术和显存复用策略,解决大模型推理时的内存瓶颈问题,为批处理提供更大空间。

量化技术
  • 动态4位量化:将模型参数从FP32压缩为4位整数,显存占用减少70%。例如,Llama-3.1(8B)在4位量化后仅需7GB显存即可运行,允许在消费级显卡(如RTX 4090)上处理更大批次。
  • 混合量化:对部分关键层(如注意力层)保持16位精度,其余层使用4位量化,在精度和效率间取得平衡。例如,Qwen2.5(7B)在混合量化后,BLEU评分仅下降1.2%,但吞吐量提升1.8倍。
显存复用
  • KV缓存优化:动态管理键值缓存(Key-Value Cache),避免重复计算。例如,处理连续对话时,缓存历史token的中间结果,批次内请求共享缓存数据,减少显存占用30%。
  • 梯度检查点:选择性存储中间激活值,需要时重新计算,进一步节省显存。例如,在微调Llama-3.3 70B时,显存占用从80GB降至48GB,允许更大批次的推理。

与vLLM集成的端到端优化

Unsloth与vLLM推理框架深度集成,实现动态批处理与实时推理的协同优化。

动态请求合并
  • Continuous Batching:将不同长度的请求动态合并为批次,减少填充(Padding)带来的计算浪费。例如,处理长度为100和200的请求时,自动填充到200,批次内总token数提升40%。
  • 流式输出:支持边生成边输出,在批次处理过程中逐步返回结果,降低延迟。例如,生成1000 tokens的响应时,每生成50 tokens即可返回部分结果,用户等待时间缩短50%。
性能监控与调优
  • 吞吐量-延迟平衡:通过参数配置(如max_batch_size=32max_queue_len=100)动态调整批次大小和队列长度。例如,在A100 80GB GPU上,将批次大小从16调整为32,吞吐量提升25%,但延迟增加15%。
  • 硬件感知优化:自动检测GPU架构(如Ampere、Hopper),选择最优的计算内核和内存分配策略。例如,在H100上启用Tensor Core加速,吞吐量提升3倍。

实际应用案例与效果

案例1:客服对话系统
  • 场景:处理数万用户的实时咨询请求。
  • 配置:使用Llama-3.1(8B),4位量化,批次大小16,vLLM动态调度。
  • 效果:吞吐量达每秒3000 tokens,响应延迟低于500ms,显存占用仅15GB。
案例2:代码生成
  • 场景:处理长代码片段的补全请求。
  • 配置:使用CodeLlama(7B),混合精度,批次大小8,梯度检查点启用。
  • 效果:生成1000行代码的时间从12秒降至4秒,显存占用减少60%。
案例3:医疗影像报告生成
  • 场景:处理高分辨率医学影像的分析请求。
  • 配置:使用Mistral-7B,动态4位量化,批次大小4,长上下文支持。
  • 效果:处理512x512影像的报告生成时间从8秒降至3秒,显存占用从20GB降至7GB。

参数调优与最佳实践

关键参数说明
参数名称描述推荐值范围
batch_size单次处理的请求数8-64(根据显存调整)
max_seq_length最大上下文长度2048-32768(根据模型支持)
quantization量化精度(4bit/8bit/FP16)4bit(默认)
gradient_checkpointing是否启用梯度检查点(节省显存)True/False
stream_output是否启用流式输出True
调优步骤
  1. 显存压力测试:逐步增加批次大小,观察显存占用和吞吐量的变化,找到临界点。
  2. 延迟敏感型场景:优先降低批次大小(如4-8),启用流式输出。
  3. 吞吐量优先场景:增大批次大小(如32-64),启用混合量化。
  4. 长上下文场景:启用RoPE扩展,调整max_seq_length至模型支持上限。

常见问题与解决方案

问题现象可能原因解决方案
显存不足批次过大或未启用量化降低批次大小,启用4位量化
延迟过高批次合并等待时间过长减小max_queue_len参数
吞吐量低硬件未充分利用启用混合精度,优化内核参数
模型输出异常量化精度不足或缓存策略错误关闭量化,调整KV缓存参数

二、对比模型并行与流水线并行在分布式推理中的部署难度。

以下是模型并行与流水线并行在分布式推理中部署难度的对比分析,结合硬件依赖、通信开销、负载均衡等核心维度,通过表格形式呈现关键差异:

分布式推理并行策略部署难度对比表

维度模型并行(Model Parallelism)流水线并行(Pipeline Parallelism)
硬件要求必须依赖高速GPU互联(如NVLink),否则通信延迟显著影响性能。
例:70B模型需4张40GB GPU通过NVLink互联。
对通信带宽要求较低,普通PCIe即可支持。
例:3阶段流水线可部署在3台普通服务器(PCIe 4.0)。
通信开销每层计算后需同步中间结果(如AllReduce),通信频率极高。
在无NVLink场景下,通信时间占比可达总耗时的50%以上。
仅在阶段间传递激活值,通信量较小。
例如:7B模型的层间激活值传输量约为权重参数的1/10。
负载均衡层计算量差异可能导致设备利用率不均(如注意力层 vs 全连接层)。
需手动调整层分配策略,优化难度高。
阶段计算时间需严格平衡,否则产生“流水线气泡”。
例如:若阶段1耗时10ms,阶段2耗时5ms,整体吞吐量受限为20ms/批次。
编程复杂度需手动分割模型层或使用特定框架(如vLLM张量并行)。
例:Llama-3.3 70B需按层分配到8个GPU,配置参数超过50个。
需设计阶段划分和微批次调度,动态管理数据流。
例:GPT-4的120层需划分为8个阶段,每个阶段处理15层。
调试难度层间通信错误难以定位(如参数同步失败)。
需结合nvidia-smi和NCCL调试工具分析GPU利用率。
阶段间数据流异常难以排查(如激活值格式不匹配)。
需使用分布式跟踪工具(如Dask)监控流水线状态。
扩展性增加GPU数量可能受限于通信带宽,线性扩展能力差。
例:从4卡扩展到8卡时,吞吐量仅提升30%。
可通过增加阶段或复制阶段扩展,理论扩展性更高。
例:8阶段流水线扩展到16阶段时,吞吐量提升约70%。
容错性单GPU故障可能导致整个模型推理中断,需重新计算。
恢复时间与模型大小成正比(如70B模型恢复需5-10分钟)。
单阶段故障仅影响当前批次,可通过重试或切换备用阶段恢复。
例:阶段3故障时,系统自动将该批次路由至备用阶段3。
内存管理显存占用分散,但需处理参数分片冲突。
例:Llama-3.3 70B在4卡张量并行时,每卡显存占用约35GB。
需存储中间激活值,总内存需求可能更高。
例:GPT-4的120层流水线在8卡部署时,激活值缓存占用约200GB。
训练/推理兼容性训练与推理部署差异较小,参数同步逻辑可复用。
例:vLLM的张量并行可直接用于训练和推理。
训练需处理反向传播和梯度同步,推理需简化调度逻辑。
例:DeepSpeed的流水线并行在训练时需额外配置梯度检查点。
框架支持主流框架(PyTorch/TensorFlow)原生支持较好。
例:PyTorch的DistributedDataParallel支持张量并行。
依赖特定库或自定义实现(如Megatron-LM、DeepSpeed)。
例:DeepSpeed的流水线并行需手动配置阶段划分。

典型场景选择建议

场景需求推荐策略原因
超大模型(如70B以上)显存不足模型并行显存扩展能力强,支持单卡无法容纳的模型。
实时推理(如客服对话)流水线并行微批次处理减少延迟,支持边生成边输出。
多节点集群(跨机部署)流水线并行减少跨机通信次数,适合长距离网络。
硬件资源有限(如普通PCIe)流水线并行对通信带宽要求低,可在普通硬件上运行。
高容错性要求(如金融风控)流水线并行单阶段故障不影响全局,恢复机制更灵活。

性能调优关键参数

策略参数作用
模型并行tensor_parallel_size控制GPU数量,需与硬件互联能力匹配。
allreduce_alg选择通信算法(如NCCL、MPI),优化同步效率。
流水线并行microbatch_size平衡计算与通信,推荐值为8-32。
num_stages阶段数量,需与模型层数匹配(如120层划分为8阶段)。

通过上述对比可见,模型并行在显存扩展和框架支持上更具优势,但对硬件要求苛刻;流水线并行在延迟优化和容错性上表现更好,但需要更复杂的调度逻辑。实际部署时需根据模型规模、硬件条件和业务需求综合选择,必要时可采用混合并行策略(如模型并行+流水线并行)。


三、如何通过llama - factory的推理脚本实现流式输出(Stream Output)

核心实现原理

llama - factory 的流式输出基于 vLLM 推理引擎OpenAI 风格 API 实现。vLLM 支持高效的流式生成,通过 stream=True 参数触发逐 token 输出。其核心机制包括:

  1. 动态 batch 管理:vLLM 会将多个请求合并为一个 batch 处理,但流式输出时会优先处理单个请求,确保实时性。
  2. KV cache 优化:vLLM 复用历史 token 的键值缓存,避免重复计算,提升生成速度。
  3. HTTP 长连接:API 接口通过 HTTP chunked 传输协议实现增量输出,客户端可实时接收数据。

具体操作步骤

1. 启动推理服务

在 llama - factory 中,流式输出依赖于 vLLM 的推理服务。通过以下命令启动:

llamafactory-cli serve --model-name-or-path your_model_path --streaming True
  • --streaming True 开启流式输出支持
  • 服务默认监听端口 8000,可通过 --port 参数修改
2. API 调用示例

使用 OpenAI 风格的接口进行流式请求:

import openai
openai.api_base = "http://localhost:8000/v1"

response = openai.ChatCompletion.create(
    model="your_model_name",
    messages=[
        {"role": "user", "content": "写一个 Python 冒泡排序函数"}
    ],
    stream=True  # 关键参数,启用流式输出
)

for chunk in response:
    delta = chunk['choices'][0]['delta']
    if 'content' in delta:
        print(delta['content'], end='', flush=True)
  • 输出结果会以 {"role":"assistant","content":"..." 的 JSON 块形式逐字符返回
  • 客户端需处理断包和连接保持问题
3. 命令行工具使用

llama - factory 提供了 llamafactory-cli 工具支持流式输出:

llamafactory-cli chat --prompt "写一个 Python 冒泡排序函数" --stream
  • --stream 参数启用流式输出
  • 输出格式为纯文本,自动拼接各 token

关键参数配置

1. 模型相关参数
参数名称描述推荐值
max_new_tokens生成的最大 token 数512-2048
temperature采样温度,控制输出多样性(0.0-1.0)0.7
top_p核采样参数,控制输出随机性(0.0-1.0)0.95
2. 流式输出优化参数
参数名称描述推荐值
stream_interval输出间隔时间(秒)0.01-0.1
timeout连接超时时间(秒)300
chunk_size每次传输的 token 数16-64

高级功能扩展

1. 自定义输出格式

通过修改 src/llamafactory/api/vllm_server.py 中的 generate 函数,可自定义输出格式:

async def generate(request: Request):
    # 原有代码
    ...
    # 添加自定义处理逻辑
    for chunk in response:
        custom_output = process_chunk(chunk)  # 自定义处理函数
        yield json.dumps(custom_output)
2. 多语言支持

llamafactory/data/template.py 中配置多语言模板:

TEMPLATE_MAPPING = {
    "zh": {
        "prompt": "请用中文回答:{query}",
        "stream_prefix": "回答:"
    },
    "en": {
        "prompt": "Please answer in English: {query}",
        "stream_prefix": "Answer: "
    }
}
3. 性能优化
  1. 模型量化:使用 --quantization-bit 4 进行 4 位量化,减少显存占用
  2. 模型并行:通过 --num-gpus 参数启用多卡并行推理
  3. 缓存策略:设置 --max-batch-size 调整 batch 大小,平衡吞吐量和延迟

四、解释vllm的--trust_remote_code参数在加载自定义模型时的作用和风险。

1. 支持自定义代码执行

在加载模型时,某些模型可能会依赖于一些自定义的代码逻辑。例如,一些模型使用了特殊的架构或者自定义的前向传播方法,这些代码并不是 transformers 库中默认支持的。当你使用 --trust_remote_code 参数时,vLLM 会允许从远程源加载并执行这些自定义代码。这样就能让 vLLM 顺利加载和运行那些需要特殊代码实现的模型。比如,某个研究团队发布了一个新的语言模型,该模型采用了全新的注意力机制,并且提供了实现该机制的自定义代码。在加载这个模型时,使用 --trust_remote_code 就可以让 vLLM 执行这些代码,从而正确地加载和使用该模型。

2. 灵活性和扩展性

这个参数为模型的使用提供了更大的灵活性和扩展性。研究人员和开发者可以自由地探索和使用最新的模型架构和技术。即使这些模型的实现不在标准库的支持范围内,也能够借助 --trust_remote_code 来加载和运行它们。这有助于推动自然语言处理领域的创新,因为它允许社区更快地采用新的研究成果。

使用 --trust_remote_code 参数的风险

虽然 --trust_remote_code 带来了很多便利,但也伴随着一些不可忽视的风险。

1. 安全风险

加载和执行远程代码是一项具有潜在危险的操作。因为你无法完全确定远程代码的来源和安全性。恶意代码可能会利用这个机会进行攻击,例如窃取系统中的敏感信息(如用户数据、系统配置等)、修改系统文件、在后台运行恶意程序等。如果攻击者将恶意代码嵌入到自定义模型的代码中,当你使用 --trust_remote_code 参数加载该模型时,就可能会遭受攻击。

2. 兼容性问题

远程代码可能与你本地的环境存在兼容性问题。这些代码可能依赖于特定版本的库或者操作系统,而你的本地环境可能并不满足这些要求。这可能会导致模型加载失败、运行时出错或者产生不可预期的结果。例如,远程代码可能使用了某个库的最新版本的特定功能,而你本地安装的是旧版本的库,就会出现兼容性问题。

3. 维护成本增加

使用自定义代码会增加模型的维护成本。因为你不仅要关注模型本身的更新,还要关注自定义代码的更新和维护。如果自定义代码的开发者停止维护或者更新代码,可能会导致模型在未来无法正常使用。此外,当出现问题时,排查和解决问题的难度也会增加,因为你需要同时考虑模型和自定义代码的因素。

在使用 --trust_remote_code 参数时,要谨慎权衡其带来的好处和风险。如果可能的话,尽量从可信任的来源获取自定义代码,并且在使用之前对代码进行仔细的审查和测试。


五、如何通过DeepSpeed的--bf16参数启用BF16精度训练?

要通过 DeepSpeed 的 --bf16 参数启用 BF16(Brain Floating Point 16)精度训练,可按以下步骤进行:

1. 环境准备

首先,要保证你的环境已经正确安装了 DeepSpeed 以及相关的深度学习框架(如 PyTorch)。可以使用如下命令安装 DeepSpeed:

pip install deepspeed

2. 配置 DeepSpeed 配置文件

DeepSpeed 通常需要一个配置文件来指定训练过程中的各种参数。创建一个 JSON 格式的配置文件,例如 deepspeed_config.json,在其中设置启用 BF16 精度训练。示例配置文件内容如下:

{
    "train_batch_size": 64,
    "optimizer": {
        "type": "Adam",
        "params": {
            "lr": 0.0001
        }
    },
    "fp16": {
        "enabled": false
    },
    "bf16": {
        "enabled": true
    }
}

在上述配置文件中:

  • train_batch_size 是训练时的批次大小。
  • optimizer 部分定义了优化器的类型和参数,这里使用的是 Adam 优化器,学习率为 0.0001。
  • fp16enabled 设为 false,表明不启用 FP16 精度训练。
  • bf16enabled 设为 true,以此来启用 BF16 精度训练。

3. 修改训练脚本

要确保你的训练脚本与 DeepSpeed 兼容。下面是一个简单的 PyTorch 训练脚本示例,展示了如何集成 DeepSpeed 并启用 BF16 精度训练:

import torch
import deepspeed

# 初始化模型
model = ...  # 这里需要替换为你的模型定义
parameters = filter(lambda p: p.requires_grad, model.parameters())

# 初始化 DeepSpeed
model, optimizer, _, _ = deepspeed.initialize(
    model=model,
    model_parameters=parameters,
    config='deepspeed_config.json'
)

# 训练循环
for epoch in range(num_epochs):
    for inputs, labels in dataloader:
        outputs = model(inputs)
        loss = ...  # 这里需要替换为你的损失函数计算
        model.backward(loss)
        model.step()

在这个脚本中:

  • 首先定义了模型和可训练的参数。
  • 然后使用 deepspeed.initialize 函数来初始化 DeepSpeed,传入模型、参数和之前创建的 DeepSpeed 配置文件路径。
  • 最后在训练循环中进行前向传播、损失计算、反向传播和参数更新。

4. 启动训练

使用 deepspeed 命令来启动训练脚本,并通过 --bf16 参数启用 BF16 精度训练。示例命令如下:

deepspeed --bf16 train.py

这里的 train.py 是你的训练脚本文件名。

注意事项

  • 硬件支持:要确保你的 GPU 支持 BF16 数据类型。像 NVIDIA A100、H100 等 GPU 都支持 BF16 运算,使用支持 BF16 的 GPU 才能发挥出 BF16 精度训练的优势。
  • 数值稳定性:BF16 的动态范围比 FP16 大,但在某些情况下,仍然可能会遇到数值稳定性问题。如果在训练过程中发现损失值出现异常或者模型不收敛,可能需要调整超参数或者采用其他技术来提高数值稳定性。
  • 兼容性:要确保你的深度学习框架版本与 DeepSpeed 兼容,避免因为版本不兼容而导致训练失败。

六、对比不同推理引擎(TensorRT vs ONNX Runtime)在vllm中的集成效果

对比维度TensorRT 在 vLLM 中的集成效果ONNX Runtime 在 vLLM 中的集成效果
性能表现- 高度优化的推理引擎,专为 NVIDIA GPU 设计。在 vLLM 中集成后,可利用 TensorRT 的张量核心进行高效计算,对于大规模语言模型推理,能显著提升推理速度,尤其是在批量推理场景下,可实现极低的延迟和高吞吐量。
- 支持 INT8、FP16 等低精度推理,能进一步降低计算量和内存占用,提升性能。
- 跨平台支持良好,可在多种硬件上运行,包括 CPU、GPU 等。在 vLLM 中使用 ONNX Runtime 时,可根据硬件资源灵活选择计算后端。
- 推理性能在不同硬件上较为稳定,但相比 TensorRT 在 NVIDIA GPU 上的优化程度,其在 NVIDIA GPU 上的性能提升可能相对有限。不过在 CPU 上的推理表现有一定优势。
模型兼容性- 对模型结构有一定要求,需要将模型转换为 TensorRT 引擎格式。某些复杂模型结构可能需要额外的处理才能与 TensorRT 兼容。
- 对于一些特定的算子和层,可能需要进行自定义实现或适配。
- 支持 ONNX 格式的模型,具有良好的模型兼容性。大部分深度学习框架训练的模型都可以转换为 ONNX 格式,然后使用 ONNX Runtime 进行推理,在 vLLM 中集成时,模型转换过程相对简单。
开发难度- 集成过程相对复杂,需要对 TensorRT 的 API 有深入了解。包括模型转换、引擎构建、推理流程等步骤,都需要进行详细的配置和优化。
- 对于开发者的技术要求较高,需要具备一定的 GPU 编程和优化经验。
- 集成相对简单,ONNX Runtime 提供了简洁的 API 接口,便于开发者使用。只需要将模型转换为 ONNX 格式,然后调用相应的 API 进行推理即可。
- 开发文档丰富,社区支持良好,降低了开发难度。
灵活性- 灵活性相对较低,主要针对 NVIDIA GPU 进行优化,对于其他硬件平台的支持有限。一旦选择了 TensorRT,就需要依赖 NVIDIA 的硬件生态。- 具有较高的灵活性,可根据不同的硬件资源选择合适的计算后端,如 CPU、NVIDIA GPU、AMD GPU 等。可以在不同的环境中快速部署和运行模型。
成本- 需要 NVIDIA GPU 硬件支持,硬件成本较高。同时,对于大规模推理场景,可能需要多 GPU 配置,进一步增加了成本。- 硬件选择较为灵活,可根据实际需求选择不同的硬件平台,成本相对较低。例如,对于一些对性能要求不高的场景,可以选择使用 CPU 进行推理。

七、解释在llama - factory中如何通过--lora参数加载预训练的LoRA权重

1. 确保环境准备就绪

在安装 peft 库时,最好指定具体版本,因为不同版本的 peft 可能对某些功能支持不同。同时,强调 peft 库与 transformers 库版本的兼容性也很重要,因为它们之间可能存在版本不兼容问题。以下是更完善的环境准备步骤:

# 建议指定 peft 库版本,这里以 0.4.0 为例,可根据实际情况调整
pip install peft==0.4.0

2. 准备预训练的 LoRA 权重

在实际操作中,预训练的 LoRA 权重可能有不同的存储格式和文件结构。可以补充一些检查和验证步骤,确保权重文件的完整性和正确性。例如:

# 检查 LoRA 权重目录是否存在
if [ ! -d "./lora_weights" ]; then
    echo "LoRA 权重目录不存在,请检查路径。"
    exit 1
fi

# 检查 LoRA 权重目录中是否包含必要的文件,如 adapter_config.json 和 adapter_model.bin
if [ ! -f "./lora_weights/adapter_config.json" ] || [ ! -f "./lora_weights/adapter_model.bin" ]; then
    echo "LoRA 权重文件不完整,请检查。"
    exit 1
fi

3. 启动服务时加载 LoRA 权重

在启动 llamafactory-cli serve 命令时,可能会遇到一些常见错误,比如模型路径错误、权限问题等。可以补充一些错误处理和提示信息:

# 启动服务并加载 LoRA 权重
llamafactory-cli serve --model-name-or-path your_base_model_path --lora ./lora_weights

# 检查服务启动是否成功,可根据实际的错误输出进行更详细的判断
if [ $? -ne 0 ]; then
    echo "服务启动失败,请检查模型路径和 LoRA 权重路径是否正确,以及是否有足够的权限。"
fi

4. 代码调用示例

在代码调用示例中,可以添加一些异常处理,增强代码的健壮性。同时,对于 GPU 设备的使用可以进行明确指定:

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

try:
    # 加载基础模型和分词器
    base_model_path = "your_base_model_path"
    model = AutoModelForCausalLM.from_pretrained(base_model_path)
    tokenizer = AutoTokenizer.from_pretrained(base_model_path)

    # 加载 LoRA 权重
    lora_path = "./lora_weights"
    model = PeftModel.from_pretrained(model, lora_path)

    # 将模型移动到 GPU 上(如果有可用的 GPU)
    device = "cuda" if torch.cuda.is_available() else "cpu"
    model = model.to(device)

    # 进行推理
    input_text = "请介绍一下人工智能"
    input_ids = tokenizer(input_text, return_tensors="pt").input_ids.to(device)
    output = model.generate(input_ids)
    output_text = tokenizer.decode(output[0], skip_special_tokens=True)
    print(output_text)
except Exception as e:
    print(f"推理过程中出现错误: {e}")

相关文章:

  • io_uring 异步 socket 编程
  • 自动化框架及其设计搭建浅谈(二)--分层自动化测试
  • 浮点数精度问题
  • vue项目中,添加主题皮肤切换功能
  • 负指数二项式展开
  • 【Unity】animator检测某state动画播放完毕方法
  • C高级,终端操作
  • 如何保证RabbitMQ消息的可靠传输?
  • 【Scratch编程系列】程序积木-外观类
  • 1101复位之后故障仍保持原因分析
  • 交换机和集线器的区别
  • Compose笔记(十四)--LazyColumn
  • 计算机系统---性能指标(1)CPU与GPU
  • 横扫SQL面试——PV、UV问题
  • 语法: i8=make8( var, offset);
  • 3D Gaussian Splatting as MCMC 与gsplat中的应用实现
  • Python面向对象-开闭原则(OCP)
  • Access:在移动互联网与AI时代焕发新生
  • ReFormX:现代化的 React 表单解决方案 - 深度解析与最佳实践
  • sojson。v5:新一代JavaScript代码保护工具的技术解析与应用场景
  • 绵阳市中医医院网站建设/餐饮营销策划与运营
  • 手机网站模板 餐饮/关键词搜索热度查询
  • 如何跟进网站建设的客户/班级优化大师免费下载电脑版
  • wordpress 多站点设置/360搜索推广官网
  • 大连庄河网站建设/网络营销研究现状文献综述
  • 潍坊外贸网站制作/软件开发交易平台