Linearis:现代高性能线性代数库的设计原理与异构计算实践

news2026/4/27 5:10:35
1. 项目概述与核心价值最近在开源社区里一个名为Linearis的项目引起了我的注意。它来自仓库linearis-oss/linearis定位是一个“现代、高性能的线性代数库”。初看这个描述你可能会觉得线性代数库已经多如牛毛从经典的 BLAS/LAPACK 到现代的 Eigen、Armadillo再到深度学习框架内置的 TensorFlow/PyTorch似乎没有太多新意。但当我深入探究其设计哲学和实现细节后发现 Linearis 试图在几个关键痛点上做出突破它瞄准的不是“又一个库”而是一个旨在解决现代科学计算和机器学习工作流中从研究到部署全链路效率问题的“基础设施”。简单来说Linearis 的核心目标是为异构计算环境CPU、GPU甚至未来的专用加速器提供一个统一、零拷贝、且对开发者极度友好的线性代数接口。它不仅仅关注计算本身的性能更关注数据在内存中的流动成本、API 的易用性以及与现有生态的无缝集成。这听起来像是一个宏大的愿景但 Linearis 通过一系列精巧的设计正在将其变为现实。如果你是一名经常与矩阵、向量打交道的研究员、算法工程师或者是在构建需要高性能数值计算的后端服务开发者那么理解 Linearis 能为你带来什么或许能显著提升你的工作效率和系统性能。2. 核心设计思路与架构拆解2.1 统一内存模型与零拷贝哲学传统线性代数库的一个巨大痛点在于数据搬运。例如你用 NumPy 在 CPU 上准备了一个大矩阵现在想用 CuPy 在 GPU 上进行计算。通常的流程是numpy_array - 通过.get()或显式拷贝 - cupy_array。这个拷贝过程尤其是对于大规模数据会成为性能瓶颈并且增加了代码的复杂性。Linearis 从设计之初就致力于消除这种不必要的拷贝。其基石是一个统一的内存抽象层。它并不直接管理物理内存而是作为一个“视图”或“句柄”可以透明地引用存在于不同设备CPU、GPU 统一内存、GPU 独立显存上的数据。当你创建一个 Linearis 张量时你需要指定一个“后端”如cpucuda和一个“内存句柄”。这个句柄可以来自已有的 NumPy 数组、PyTorch 张量、CUDA 指针甚至是原始内存地址。关键实现Linearis 内部使用一个轻量级的Tensor对象它主要包含元数据形状、数据类型、步长和一个指向实际内存的Buffer对象。Buffer对象本身不拥有数据它只是一个带有引用计数的智能指针指向由具体后端内存分配器管理的内存块。当你在不同后端间进行运算时只要它们支持相同的内存空间例如都支持 CUDA 统一内存Linearis 会尽可能安排计算在该内存空间内直接进行避免物理拷贝。注意这里的“零拷贝”是逻辑上的。如果两个计算后端确实无法直接访问同一块物理内存例如一个数据在 CPU 的普通 RAM另一个计算需要在 NVIDIA GPU 的显存那么 Linearis 仍然需要执行一次拷贝。但其优势在于这个拷贝是库内部、优化过的并且对用户透明。用户代码始终是统一的A B而不需要关心A和B具体在哪。2.2 多后端支持与计算分派为了达成“统一接口异构执行”的目标Linearis 采用了多后端动态分派的架构。目前它主要支持CPU 后端通常基于高效的 BLAS 库如 OpenBLAS, MKL实现核心的矩阵运算。CUDA 后端利用 NVIDIA GPU 的并行计算能力通过 CUDA 和 cuBLAS/cuSOLVER 等库进行加速。其他后端架构上预留了接口可以相对容易地集成其他硬件后端如 AMD ROCm、Apple Metal甚至是 FPGA。其工作流程如下张量创建用户指定后端如backend‘cuda’创建张量或从现有数据如 PyTorch CUDA 张量导入。操作符重载用户使用 Python 的运算符,-,,*或丰富的函数 APIlin.matmul,lin.inv进行计算。动态分派Linearis 的运行时系统会检查参与运算的所有张量的后端、数据类型、内存布局。内核选择根据上述信息选择最优的计算内核Kernel。例如两个 CUDA 后端 float32 类型的矩阵进行乘法会自动选择 cuBLAS 的sgemm例程。异步执行与流管理针对 GPU为了最大化 GPU 利用率Linearis 的 CUDA 后端支持操作异步执行和 CUDA 流Stream管理允许计算与 CPU 任务或 GPU 上的其他计算重叠。这种设计的好处是用户可以用一套代码通过简单地切换张量的创建后端就能让整个计算流程在 CPU 或 GPU 上运行无需重写算法逻辑。2.3 与现有生态的深度集成一个库再好如果无法融入现有生态学习成本和迁移成本也会让人望而却步。Linearis 在这方面做得相当出色。它将自己定位为“胶水层”或“桥梁”而非替代者。与 NumPy 互操作Linearis 张量可以零拷贝地从 NumPy 数组创建也可以零拷贝地转换为 NumPy 数组。这意味着你可以轻松地将 Linearis 插入到现有的基于 NumPy 的数据处理流水线中只对计算密集的部分进行加速。import numpy as np import linearis as lin # 从 NumPy 零拷贝创建 Linearis CPU 张量 np_arr np.random.randn(1000, 1000).astype(np.float32) lin_tensor lin.from_numpy(np_arr) # 或 lin.asarray(np_arr, backend‘cpu’) # 对 lin_tensor 进行高速运算... # 再零拷贝转回 NumPy result_np lin.to_numpy(lin_tensor)与 PyTorch/TensorFlow/JAX 互操作思路类似。Linearis 提供了与这些框架张量的内存共享机制。例如你可以获取一个 PyTorch CUDA 张量的底层数据指针用它创建一个 Linearis CUDA 张量然后用 Linearis 的特定函数进行计算结果会直接反映在原始的 PyTorch 张量中。这允许你混合使用不同框架的优势用 PyTorch 做自动微分和模型训练用 Linearis 做某些定制化的、高性能的线性代数运算。这种深度集成使得 Linearis 可以作为一个“性能补丁”被渐进式地采纳到现有项目中而不是要求全盘推翻重来。3. 核心 API 解析与实操指南3.1 张量创建与内存管理Linearis 的核心对象是Tensor。创建张量有多种方式对应不同的使用场景。1. 从已有数据创建零拷贝这是最常用、最高效的方式。确保数据已经在某个后端的内存中。import linearis as lin import numpy as np import torch # 从 NumPy 数组创建 (CPU后端) np_data np.ones((5, 5), dtypenp.float64) tensor_from_np lin.asarray(np_data, backend‘cpu’) print(tensor_from_np.device) # 输出: cpu # 从 PyTorch CPU 张量创建 torch_cpu torch.ones(5, 5, dtypetorch.float32) tensor_from_torch_cpu lin.from_dlpack(torch_cpu) # 使用 DLPack 协议交换 # 或使用特定后端的 from_buffer 函数更底层 # 从 PyTorch CUDA 张量创建 if torch.cuda.is_available(): torch_cuda torch.ones(5, 5, dtypetorch.float32, device‘cuda’) tensor_from_torch_cuda lin.asarray(torch_cuda, backend‘cuda’) print(tensor_from_torch_cuda.device) # 输出: cuda:02. 分配新内存创建当你需要一块全新的、特定形状和类型的缓冲区时使用。# 在 CPU 上分配一个未初始化的 3x3 单精度浮点矩阵 tensor_cpu lin.empty((3, 3), dtypelin.float32, backend‘cpu’) # 在 GPU 上分配一个全零的 2x2x2 三维张量 tensor_gpu lin.zeros((2, 2, 2), dtypelin.int64, backend‘cuda’) # 在 CPU 上分配一个单位矩阵 eye_mat lin.eye(4, dtypelin.float64, backend‘cpu’)3. 内存布局与步长Stride高级用户有时需要处理非连续内存的数据如矩阵的某一行、某一列或者转置后的视图。Linearis 支持自定义步长。# 创建一个 2D 张量但内存是行优先C-contiguous的连续块 a lin.ones((3, 4), backend‘cpu’) print(a.is_contiguous()) # 通常为 True # 获取它的转置视图零拷贝 a_t a.T print(a_t.shape) # (4, 3) print(a_t.is_contiguous()) # 通常为 False因为内存不是连续访问的 # 但计算时库会自动处理非连续内存可能涉及临时拷贝或使用特定的内核。实操心得优先使用lin.asarray()或lin.from_numpy()从现有数据创建这是发挥 Linearis 零拷贝优势的关键。仅在需要全新内存时才使用lin.empty或lin.zeros。同时注意dtype的匹配不匹配的dtype会在运算时引发隐式类型转换可能带来额外开销。3.2 基本运算与广播机制Linearis 支持丰富的元素级运算和矩阵运算语法设计上借鉴了 NumPy学习成本很低。元素级运算支持,-,*,/,//,%,**等运算符以及lin.sin,lin.exp,lin.log等数学函数。这些运算都支持广播Broadcasting。a lin.ones((2, 3), backend‘cpu’) b lin.full((2, 1), 2.0, backend‘cpu’) # 形状 (2, 1) # 广播b 会在第二维上复制与 a 进行元素乘法 c a * b # c.shape (2, 3)每个元素是 1 * 2 2 print(c)矩阵线性代数运算这是 Linearis 的强项。提供了高度优化的例程。矩阵乘法使用运算符或lin.matmul函数。A lin.random.randn(100, 50, backend‘cuda’) B lin.random.randn(50, 30, backend‘cuda’) C A B # 在 GPU 上执行矩阵乘法自动调用 cuBLAS # 等价于 C lin.matmul(A, B)求解线性系统lin.solve。对于小规模或特殊矩阵也有lin.inv求逆通常不推荐直接求逆。# 求解 A * x B A lin.array([[2, 1], [1, 2]], dtypelin.float64, backend‘cpu’) B lin.array([[5], [4]], dtypelin.float64, backend‘cpu’) x lin.solve(A, B) # 使用 LU 分解等方法求解 print(x)分解lin.lu,lin.qr,lin.svd,lin.cholesky等。这些函数返回的是分解后的因子元组而不是直接计算结果内存效率更高。A lin.random.randn(5, 5, backend‘cpu’) P, L, U lin.lu(A) # P 是置换矩阵L 是下三角U 是上三角原地运算In-place为了减少内存分配一些运算支持原地操作在函数名后加下划线如add_,mul_。a lin.ones((5, 5), backend‘cpu’) b lin.full((5, 5), 3.0, backend‘cpu’) a.add_(b) # a 的内容变为 a b没有创建新的张量 print(a[0,0]) # 输出 4.0注意事项原地运算会修改原始数据需谨慎使用。确保在后续计算中不再需要原始数据。3.3 切片、索引与高级操作和 NumPy 类似Linearis 支持灵活的切片和索引并且大多数情况下是零拷贝的视图操作。t lin.arange(12, backend‘cpu’).reshape(3, 4) # 创建 3x4 矩阵 print(t) # 输出类似 # [[ 0 1 2 3] # [ 4 5 6 7] # [ 8 9 10 11]] # 切片视图 row_slice t[1, :] # 第二行形状 (4,) col_slice t[:, 2] # 第三列形状 (3,) sub_matrix t[0:2, 1:3] # 前两行第2、3列形状 (2, 2) # 布尔索引 mask t 5 filtered t[mask] # 返回一维张量包含所有大于5的元素 print(filtered) # 花式索引Fancy indexing # 注意花式索引通常**不是**视图会产生数据拷贝。 indices lin.array([0, 2], dtypelin.int64, backend‘cpu’) selected_rows t[indices, :] # 选择第1行和第3行重塑reshape与转置transposereshape在数据连续时通常是视图否则会产生拷贝。T属性或transpose()函数返回的是视图。a lin.arange(6, backend‘cpu’).reshape(2, 3) b a.reshape(3, 2) # 视图因为 arange 创建的数据是连续的 c a.T # 视图4. 性能调优与高级特性实战4.1 后端选择与设备间数据传输选择正确的后端是性能优化的第一步。基本原则是数据量大、计算密集的操作优先使用 GPUCUDA 后端控制逻辑复杂、数据量小的操作使用 CPU 后端更灵活。设备间数据传输优化尽管 Linearis 提倡零拷贝但跨设备如 CPU - GPU传输有时不可避免。关键是最小化传输次数和传输量。import linearis as lin import numpy as np import time # 不推荐的模式在循环中频繁传输 cpu_data np.random.randn(1000, 1000) gpu_data lin.asarray(cpu_data, backend‘cuda’) # 第一次传输 results [] for i in range(10): # 错误每次迭代都进行 CPU-GPU 传输 # small_cpu cpu_data[i:i10, :] # small_gpu lin.asarray(small_cpu, backend‘cuda’) # 正确在 GPU 上直接切片零拷贝 small_gpu gpu_data[i:i10, :] # 这是 GPU 内存上的视图 result lin.matmul(small_gpu, small_gpu.T) results.append(result) # 最终一次性将结果传回 CPU如果需要 final_results_cpu lin.to_numpy(lin.concat(results, axis0))统一内存Unified Memory的利用如果你的 GPU 支持 CUDA 统一内存如 NVIDIA Tesla P100 或消费级显卡搭配特定驱动和设置可以尝试使用backend‘cuda:um’如果 Linearis 支持。统一内存允许 CPU 和 GPU 共享同一个内存空间由系统自动迁移数据页可以简化编程模型但需要注意其性能特征页迁移有开销。4.2 利用异步执行与流重叠对于 GPU 计算充分利用其异步特性可以隐藏内存传输延迟提升整体吞吐量。Linearis 的 CUDA 后端通常支持通过stream参数进行异步操作。import linearis as lin # 假设 lin 的 CUDA 后端提供了 Stream 上下文管理器 stream lin.cuda.Stream() # 创建一个新的 CUDA 流 with lin.cuda.stream(stream): # 在这个流中的所有计算都是异步的 A lin.random.randn(1024, 1024, backend‘cuda’) B lin.random.randn(1024, 1024, backend‘cuda’) C A B # 此时GPU 正在计算 C但 CPU 可以继续做其他事情比如准备下一批数据 cpu_task_result some_cpu_function() # 如果需要 C 的结果需要同步流 stream.synchronize() # 等待流中的计算完成 # 或者当你尝试访问 C 的数据时如 to_numpy会自动触发同步 result_on_cpu lin.to_numpy(C)典型优化模式创建多个 CUDA 流。在不同的流中交替执行“数据从 CPU 拷贝到 GPU”、“GPU 计算”、“结果从 GPU 拷贝回 CPU”这三个阶段。当一个流在进行计算时另一个流可以进行数据传输从而重叠计算和通信。4.3 批处理与向量化对于大量的小型线性代数问题如成千上万个小型矩阵的乘法或求逆逐一遍历调用库函数会引入巨大的 Python 开销。Linearis 的许多函数支持批处理模式。# 假设我们有 1000 个 3x3 的矩阵需要求逆 batch_size 1000 matrices lin.random.randn(batch_size, 3, 3, backend‘cuda’) # 形状 (1000, 3, 3) # 低效做法Python循环 # inverses lin.empty_like(matrices) # for i in range(batch_size): # inverses[i] lin.inv(matrices[i]) # 高效做法批处理 # 注意lin.inv 可能原生支持批处理或者使用 lin.solve 配合单位矩阵批处理 identity_batch lin.eye(3, backend‘cuda’).expand(batch_size, 3, 3) # 扩展成批 inverses_batch lin.solve(matrices, identity_batch) # 一次调用解决所有批处理将循环从 Python 移到了高度优化的 C/CUDA 内核内部极大地减少了开销。在设计和实现算法时应尽可能将数据组织成批处理的形式。5. 常见问题排查与调试技巧在实际使用 Linearis 的过程中你可能会遇到一些典型问题。以下是一些排查思路和解决方法。5.1 内存与设备错误错误信息/现象可能原因排查与解决RuntimeError: Expected all tensors to be on the same device, but found at least two devices, ...参与运算的张量位于不同的后端设备。1. 检查所有输入张量的.device属性。2. 使用.to(backend‘target_backend’)将张量迁移到同一设备。注意跨设备.to()操作会触发数据拷贝。CUDA error: out of memoryGPU 显存不足。1. 使用lin.cuda.memory_allocated()和lin.cuda.max_memory_allocated()监控显存使用。2. 减少批量大小batch size。3. 及时释放不再需要的张量设置variable NonePython GC 或显式调用lin.cuda.empty_cache()。4. 检查是否有内存泄漏如在不该使用视图的地方创建了视图导致原始大张量无法释放。计算速度远低于预期1. 后端选择错误该用GPU用了CPU。2. 数据在CPU和GPU间频繁拷贝。3. 使用了非连续内存导致库内部需要额外拷贝。4. 计算本身太小无法掩盖GPU启动开销。1. 确认张量的.backend属性。2. 使用性能分析工具如 NVIDIA Nsight Systems, PyTorch profiler查看时间线定位拷贝操作。3. 使用tensor.is_contiguous()检查必要时用tensor.contiguous()创建连续副本有拷贝开销。4. 对于微小运算考虑在CPU上进行或累积成批处理。与 NumPy 结果有微小差异1. 不同后端如 MKL vs OpenBLAS或不同算法如 SVD 的算法选择导致的数值误差。2. CPU 和 GPU 浮点运算的细微差异非结合性。1. 这是科学计算中的常见问题。对于大多数机器学习应用1e-5或1e-6量级的相对误差是可接受的。2. 如果需要严格可重复性固定随机种子并确保所有计算在相同设备、相同数学库版本下进行。3. 使用lin.allclose()代替直接比较浮点数结果。5.2 视图与拷贝的混淆这是导致隐蔽 bug 和性能问题的常见原因。a lin.ones((5, 5), backend‘cpu’) b a.T # b 是 a 的转置视图 b[0, 0] 999 # 这会修改 a[0, 0] 的值 print(a[0, 0]) # 输出 999 c a[:3, :3] # c 是 a 的一个子矩阵视图 a[0, 0] 0 # 这也会影响 c[0, 0] print(c[0, 0]) # 输出 0心得当你通过切片、T、transpose()等操作获得一个新张量时默认得到的是视图。如果你需要一份独立的拷贝必须显式调用.copy()方法。independent_copy a.T.copy() # 现在修改 independent_copy 不会影响 a5.3 与深度学习框架混用的陷阱当 Linearis 与 PyTorch/TensorFlow 共享内存时需要特别注意生命周期管理。import torch import linearis as lin torch_tensor torch.randn(10, 10, device‘cuda’) lin_tensor lin.asarray(torch_tensor, backend‘cuda’) # 内存共享 # 场景一PyTorch 张量被释放 del torch_tensor # 引用计数减1 # 此时 lin_tensor 内部的 Buffer 可能仍持有内存如果引用计数未归零但风险很高。 # 最佳实践确保共享内存的源张量在需要它的整个生命周期内都存在。 # 场景二在 PyTorch 的 autograd 上下文中使用 Linearis 计算结果 x torch.randn(5, 5, requires_gradTrue, device‘cuda’) x_lin lin.asarray(x, backend‘cuda’) y_lin x_lin x_lin.T # 使用 Linearis 计算 y_torch torch.from_dlpack(y_lin) # 转换回 PyTorch 张量可能仍是视图 loss y_torch.sum() loss.backward() # 这里能正确计算 x 的梯度吗 # **可能不行** 因为 Linearis 的 操作对于 PyTorch 的 autograd 引擎是“不可见”的。 # PyTorch 只知道 y_torch 是从某个内存来的但不知道它是由 x 经过 matmul 计算得来的。 # 梯度会在此断开。解决方案如果需要梯度应坚持使用框架原生的运算。Linearis 更适合用于不要求自动微分的前处理、后处理或自定义的高性能内核中。或者等待 Linearis 未来可能集成的自动微分功能。6. 实战案例用 Linearis 加速一个图像处理流水线假设我们有一个简单的图像处理任务对一批图像例如1000张 256x256 的 RGB 图像进行去均值减去整个数据集的平均图像和白化PCA Whitening处理。这是一个典型的密集线性代数计算。传统 NumPy 实现CPUimport numpy as np # X shape: (1000, 256, 256, 3)假设已经加载到内存 X_np np.random.randn(1000, 256, 256, 3).astype(np.float32) # 模拟数据 # 1. 去均值 mean_image X_np.mean(axis0) # 计算平均图像形状 (256, 256, 3) X_centered X_np - mean_image # 广播减法 # 2. 重塑为二维矩阵 (样本数, 特征数) num_samples X_centered.shape[0] X_flattened X_centered.reshape(num_samples, -1) # 形状 (1000, 256*256*3) # 3. 计算协方差矩阵并做特征分解计算量巨大 cov (X_flattened.T X_flattened) / (num_samples - 1) # 形状 (特征数 特征数)这里是 196608x196608 巨大 # 由于协方差矩阵太大直接计算不现实通常采用其他方法如随机SVD。 # 此处仅为演示瓶颈。这个流程在 CPU 上特别是计算大协方差矩阵时会非常缓慢。使用 LinearisGPU加速的优化实现import linearis as lin import numpy as np # 假设数据最初在 CPU (NumPy) X_np np.random.randn(1000, 256, 256, 3).astype(np.float32) # 关键步骤1一次性将数据转移到 GPU避免后续多次传输 X_gpu lin.asarray(X_np, backend‘cuda’) # 形状 (1000, 256, 256, 3) # 1. 去均值 (在GPU上计算) mean_image_gpu X_gpu.mean(axis0) # GPU并行计算均值 X_centered_gpu X_gpu - mean_image_gpu # GPU上的广播减法 # 2. 重塑 num_samples X_centered_gpu.shape[0] X_flattened_gpu X_centered_gpu.reshape(num_samples, -1) # 视图操作零拷贝 # 3. 使用随机 SVD 进行白化避免计算大协方差矩阵 # 假设我们使用一个简化版本计算 X_flattened_gpu 的 SVD然后进行白化 # 注意对于如此大的矩阵全SVD也不可行。这里演示用一个小技巧或者调用专门的随机SVD函数。 # Linearis 可能提供了 lin.svd 或类似函数支持只计算前 k 个奇异值/向量。 # 这里我们假设只对一个小样本例如前100个进行计算以估计主成分。 sample_size 100 X_sample X_flattened_gpu[:sample_size, :] # 取前100个样本 # 计算小样本的 SVD (在GPU上) U, S, Vt lin.svd(X_sample, full_matricesFalse) # Vt 的形状 (k, 特征数) kmin(样本数特征数)100 # 利用 Vt 进行 ZCA 白化等后续操作... # ... (白化计算) ... # 4. 将最终结果传回 CPU仅在需要时 result_cpu lin.to_numpy(X_whitened_gpu) # 假设 X_whitened_gpu 是白化后的结果性能对比分析数据搬运NumPy 版本所有计算都在 CPU 内存进行。Linearis 版本在开始时有一次 CPU-GPU 的大规模数据传输之后所有中间步骤都在 GPU 内部完成最后只有一次结果回传。对于迭代算法这种“一次传输多次计算”的模式优势巨大。核心计算mean,reshape, 广播减法、矩阵乘法、SVD 等操作在 GPU 上通过高度优化的 cuBLAS、cuSOLVER 库并行执行速度可比 CPU 快数十倍甚至上百倍尤其是对于reshape和广播这种内存带宽密集型操作GPU 的高带宽优势明显。内存效率Linearis 的视图机制如reshape避免了不必要的中间拷贝。而 NumPy 的某些操作可能会产生临时数组。这个案例展示了 Linearis 的典型使用模式将数据尽早移至计算设备GPU利用其统一的 API 和零拷贝特性在设备上构建完整的高性能计算流水线最后在必要时取回结果。对于数据预处理、特征工程等环节这种加速效果尤为显著。Linearis 作为一个新兴的线性代数库其设计理念紧扣现代异构计算的痛点。它通过统一内存抽象、多后端支持和生态无缝集成为开发者提供了一个既强大又灵活的工具。虽然它在功能完备性和社区成熟度上可能与 Eigen、PyTorch 等巨擘尚有差距但其在特定场景下的性能优势和简洁的 API 设计已经显示出巨大的潜力。对于正在构建高性能数值计算应用又苦于现有库之间数据搬运成本和 API 割裂的团队来说Linearis 无疑是一个值得深入评估和尝试的选择。在实际使用中把握好“零拷贝”和“设备一致性”原则善用其与现有生态的互操作能力就能将其价值最大化。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…