CUTLASS 1.3.3中的 Volta884_h884gemm

CUTLASS 是 CUDA C++ 模板抽象的集合,用于在 CUDA 內的所有級別和規模上實現高性能矩陣-矩陣乘法 (GEMM) 和相關計算。它采用了類似于 cuBLAS 和 cuDNN 中實現的分層分解和數據移動策略。

CUTLASS 最新版本為3.3,相比1.3.3變動較大。然而重溫一下1.3.3仍然是有意義的。因為它更易于理解:

  • 與 PROGRAMMING TENSOR CORES: NATIVE VOLTA TENSOR CORES WITH CUTLASS 中介紹的內容相匹配;
  • 僅支持 Volta 一種 Tensor Core 架構;
  • Tensor Core 僅支持 half 一種數據類型;
  • 僅采用HMMA.884.F16.F16一種指令。

Demystifying Tensor Cores to Optimize Half-Precision Matrix Multiply 中提到 T4 GPU 在引入 Tensor Core 之后,原來重計算瓶頸的 GEMM 也變成了 IO 瓶頸。雖然 V100的帶寬是 T4的三倍,然而帶寬不足問題同樣存在。因此,CUTLASS 對于數據路徑進行了如下優化:

  • 全路徑128 bit 的訪問粒度:LDG.128STS.128LDS.128STD.128
  • 無沖突共享內存排列:轉置時無需填充 Shared Memory;
  • Software Pipelining:LDG.128LDS.128HMMA.884.F16.F16三種指令并行,隱藏數據移動。

下面以一個矩陣乘測例為例,介紹 Volta884_h884gemm 的實現。

TEST(Volta884_h884gemm_128x64x32_nt, 520x264x136)

OutputTile即 threadblock tile,該測例下設置為32x64x128。WarpGemmShape為32x64x64,這個是固定值。
run_gemm 初始化 Volta884GemmTraits::Params 和 GemmTestbed,調用 Gemm::launch 運行后比對結果。

TEST(Volta884_h884gemm_64x64x32_nt, 520x264x136) {typedef cutlass::gemm::Volta884GemmTraits<cutlass::MatrixLayout::kColumnMajor,cutlass::MatrixLayout::kRowMajor,cutlass::Shape<32, 64, 128>,cutlass::Shape<32, 64, 64>,half,half,half,2> GemmTraits;run_gemm<GemmTraits>(520, 264, 136);
}

CUTLASS 中 Volta884實現的層次結構如下圖所示

Gemm
Volta884GemmTraits
Volta884MultiplyAdd
GemmMainloop
Volta884Multiplicand
IdentityBlockSwizzle
GlobalLoadStreamPair
SharedStreamPair
MMAEpilogue
Volta884EpilogueTraits
PredicatedTileLoadStream
PredicatedTileStoreStream
TileStoreStream
TileLoadStream
mma
TileLoadIterator
Volta884ThreadblockMultiplicandStoreIterator
Volta884WarpMultiplicandLoadIterator
MMAGlobalLoadStream
Copy
MMASharedLoadStream

gemm_kernel_nolb

gemm_kernel_nolb
GemmMainloop::multiply_add

Kernel 函數申請動態 Shared Memory,并傳遞給 GemmMainloop,然后調用 GemmMainloop::multiply_add 進行計算。

/// GEMM kernel without launch bounds specified
template <typename Gemm_>
__global__ /* __launch_bounds__(Gemm_::kThreads) */
void gemm_kernel_nolb(typename Gemm_::Params params) {// Dynamic shared memory base pointerextern __shared__ int GemmSharedStorageBase[];// Declare pointer to dynamic shared memory.typename Gemm_::SharedStorage *shared_storage = reinterpret_cast<typename Gemm_::SharedStorage *>(GemmSharedStorageBase);// Construct the GEMM object.Gemm_ gemm(params, *shared_storage);// Run GEMM.gemm.multiply_add();
}

GemmMainloop

GemmMainloop 實現了軟流水,如下圖所示:
在這里插入圖片描述

Shared Memory 和寄存器需要兩個緩沖區,通過 SM 上的調度實現三條流水線并行。Global Memory 到 Shared Memory 的加載有同步,而從 Shared Memory 移動到寄存器時不需要同步。由于 Ampere 之前的架構不支持 Global Memory 到 Shared Memory 的直接拷貝,因此整個搬運過程比較復雜。如下圖所示,程序中多處調用 Copy::transform 函數生成transformed_fragment。原因應該是為了實現類型轉換,但 Volta 只支持 half,也就沒有實際作用。

PredicatedTileStoreStreamTileStoreStream::copy
GlobalLoadStream::commit
GlobalLoadStream::copy
Copy::transform
Volta884ThreadblockMultiplicandStoreIterator::store_post_increment
MMASharedLoadStream::copy
MMASharedLoadStream::commit
A/B
TileStoreStream::copy
TileStoreStream::copy
TileStoreStream::commit
AB
D
PredicatedTileLoadStream::copy
TileLoadStream::commit
C
Copy::transform
TileStoreIterator::store_post_increment
source_fragment
transformed_fragment
fetched_fragment
transformed_fragment
Global_Memory
Shared_Memory
fetched
transformed
Volta884MultiplyAdd
accumulators
fetched_fragment
transformed_fragment
LinearScaling
fetched_fragment
transformed_fragment
template <typename Traits_>
struct GemmMainloop {//// Type definitions///// The traits.typedef Traits_ Traits;/// The GEMM mainlooptypedef typename Traits::KernelClass KernelClass;/// The shared storage.typedef typename Traits::SharedStorage SharedStorage;/// The scalar for A.typedef typename Traits::ScalarA ScalarA;/// The scalar for B.typedef typename Traits::ScalarB ScalarB;/// The scalar in the epilogue.typedef typename Traits::Epilogue::Scalar ScalarEpilogue;/// The scalar for C.typedef typename Traits::Epilogue::ScalarC ScalarC;/// The scalar for D.typedef typename Traits::Epilogue::ScalarD ScalarD;/// The index.typedef typename Traits::Index Index;/// Define the mainloop iteration sizetypedef typename Traits::MultiplyAdd MultiplyAdd;/// The number of threads.static int const kThreads = Traits::GemmConfig::kThreads;

AccumulatorsPerWarp為 GemmConfig::AccumulatorsPerWarp 即 Volta884MultiplyAdd::WarpGemmShape,為32x64x64。
Volta884MultiplyAdd::InstructionShape 為4x32x32。因此,kWarpGemmSteps為8。

  // Number of warp-level multiply-accumulate steps executed by each warp.static Index const kWarpGemmSteps =Traits::GemmConfig::AccumulatorsPerWarp::kD / MultiplyAdd::InstructionShape::kD;/*// Make sure we have at least 2 unrolling steps or our pipeling is not going to work.static_assert(kWarpGemmSteps >= 2, "The pipelining assumes at least two steps");*//// Use the params object defined in traitstypedef typename Traits::Params Params;//// Data members///// The params.Params const& params;/// SharedStorage objectSharedStorage& shared_storage;
  //// Methods///// Ctor.CUTLASS_DEVICE GemmMainloop(Params const& params_, SharedStorage& shared_storage_): params(params_), shared_storage(shared_storage_) {}

GemmMainloop::fetch_global

GemmMainloop::fetch_global
GlobalLoadStreamPair::residue
GlobalLoadStreamPair::copy

Volta884GemmTraits::GlobalLoadStream 即 GlobalLoadStreamPair 類型。
GlobalLoadStreamPair::residue 函數調用兩次 MMAGlobalLoadStream::residue,計算在線程塊 tile 最后一次加載所需的預測掩碼。
GlobalLoadStreamPair::copy 函數調用兩次 MMAGlobalLoadStream::copy 從 Global Memory 拷貝矩陣元素到寄存器。后者調用 TileLoadIterator::load_post_increment 函數。

  /// Fetches global stream pairtemplate <bool Residue>CUTLASS_DEVICE void fetch_global(typename Traits::GlobalLoadStream& global_to_shared_stream,Index outer_k) {// If residue portion and not calculating residue in prolog, update residue predicates now.if (Residue) {global_to_shared_stream.residue(outer_k);}global_to_shared_stream.copy();}

GemmMainloop::consume_tile

如果kWarpGemmSteps小于等于4,則為kGlobalStreamFirst,先從 Global Memory 加載下一次迭代的數據。

  /// Computes a warp-level GEMM on data held in shared memorytemplate <bool Residue, bool LastIteration>CUTLASS_DEVICE void consume_tile(typename Traits::GlobalLoadStream& global_to_shared_stream,typename Traits::SharedStream& shared_load_stream,typename MultiplyAdd::Accumulators& accumulators,Index outer_k) {// Whether to load global stream before loading shared streamconst bool kGlobalStreamFirst = (kWarpGemmSteps <= 4);// Load data for the next iteration of the main loop (unless it's the last iteration).if (kGlobalStreamFirst && !LastIteration) {fetch_global<Residue>(global_to_shared_stream, outer_k);}

首先從 Shared Memory 加載下一次迭代的輸入。擁有雙緩沖區。
MMASharedLoadStream::copy 調用 Volta884WarpMultiplicandLoadIterator::load 函數加載數據到寄存器中。
問題是前一步如果沒有調用 GemmMainloop::fetch_global,從 Shared Memory 拷貝不會有問題嗎?

    CUTLASS_PRAGMA_UNROLLfor (int step = 0; step < kWarpGemmSteps; ++step) {// Trigger the copy from shared memory for the next A/B values.shared_load_stream.copy((step + 1) % kWarpGemmSteps);

如果不是kGlobalStreamFirst , 在循環的第一步時調用GemmMainloop::fetch_global 函數加載輸入。

      // Load data for the next iteration of the main loop (unless it's the last iteration).if (!kGlobalStreamFirst && (step == 0) && !LastIteration) {fetch_global<Residue>(global_to_shared_stream, outer_k);}

如果是倒數第2步,需要確保數據已經加載到了 Shared Memory。
Volta884GemmTraits::shared_load_fence 根據外部傳入的StageCount來確定是否同步線程。
GlobalLoadStreamPair::commit 函數會分別調用兩個矩陣的 GlobalLoadStream::commit 拷貝到 Shared Memory。
Volta884GemmTraits::shared_store_fence 同步線程。
MMASharedLoadStream::inc_stage 遞增stage_index

      if (step == kWarpGemmSteps - 2) {// Make sure the data from shared memory has been entirely consumed.Traits::shared_load_fence(true);global_to_shared_stream.commit();// Make sure the data is in shared memory.Traits::shared_store_fence(true);// Move to the next stage for the load (if it makes sense).shared_load_stream.inc_stage();}

MMASharedLoadStream::commit 調用 Copy 進行拷貝。Volta884WarpMultiplicandLoadIterator::Fragment 即 Fragment 。
Volta884MultiplyAdd::multiply_add 完成 Warp Tile 的計算。

      // Make sure the values are available for the current iteration to do the multiply-add.shared_load_stream.commit(step);// Do the math on the fragments of the current iteration.MultiplyAdd multiply_add;multiply_add.multiply_add(shared_load_stream.fragment_a(step),shared_load_stream.fragment_b(step),accumulators,accumulators);}}

GemmMainloop::multiply_add

Created with Rapha?l 2.3.0 GemmMainloop::multiply_add IdentityBlockSwizzle::get_threadblock_offset IdentityBlockSwizzle::get_threadblock_bounds IdentityBlockSwizzle::get_batch_id GlobalLoadStreamPair::add_batch_offset GlobalLoadStreamPair::move_to_residue GlobalLoadStreamPair::copy GlobalLoadStreamPair::commit Volta884GemmTraits::shared_store_fence GlobalLoadStreamPair::rollback SharedLoadStream::copy ClearAccumulators::clear GemmMainloop::consume_tile GemmEpilogue::epilogue End

make_Coord_from_shape 根據形狀創建一個 Coord 對象。

IdentityBlockSwizzle::get_threadblock_offset 獲得當前線程塊在輸出二維圖上的偏移。
Volta884GemmTraits::ClearAccumulators 即 ClearAccumulators。
IdentityBlockSwizzle::get_threadblock_bounds 返回 threadblock 的三維邊界。

  /// Do the GEMM.CUTLASS_DEVICE void multiply_add() {// Swizzle the IDs of the block (to enable better cache behavior).typename Traits::BlockSwizzle block_swizzle;Coord<3> threadblock_offset =block_swizzle.get_threadblock_offset(make_Coord_from_shape<typename Traits::OutputTile>());// We may want to use shared memory to clear the registers.typedef typename Traits::ClearAccumulators ClearAccumulators;// Get the bounds for each thread, it maybe different than problem_sizeCoord<3> bounds = block_swizzle.get_threadblock_bounds(params.problem_size,params.partitionK_range);

params.global_to_shared_stream即 GlobalLoadStreamPair::Params。
shared_storage.main_loop.global_to_shared_stream為 GlobalLoadStreamPair::SharedStorage。
shared_storage.main_loop.threadblock_tile為 GlobalLoadStreamPair::ThreadblockTileStorage,即 ZipTileAllocation。ZipTileAllocation::reference 返回指向數據的 ZipTensorRef 對象。
global_to_shared_stream為 Volta884GemmTraits::GlobalLoadStream 即 GlobalLoadStreamPair。
GlobalLoadStreamPair::add_batch_offset 調用 GlobalLoadStreamPair::add_batch_offset GlobalLoadStream::add_batch_offset 函數設置迭代器的 batch 偏移。

    // The streams to read A/B from global memory to shared memory.typename Traits::GlobalLoadStream global_to_shared_stream(params.global_to_shared_stream,shared_storage.main_loop.global_to_shared_stream,shared_storage.main_loop.threadblock_tile.reference(),bounds,threadblock_offset);// update A and B pointer offset based on batch_id and batch_stride_offsetglobal_to_shared_stream.add_batch_offset(block_swizzle.get_batch_id());// Create the accumulator clear.ClearAccumulators clear;

GlobalLoadStreamPair::move_to_residue 如果是在序幕中執行余數則調用 MMAGlobalLoadStream::move_to_residue 移動指針,否則直接調用 GlobalLoadStreamPair::residue 函數。
GlobalLoadStreamPair::copy 調用 MMAGlobalLoadStream::copy 函數,后者調用 TileLoadIterator::load_post_increment 加載 A 和 B 矩陣的片段到 Fragment 寄存器。
GlobalLoadStreamPair::commit 調用 MMAGlobalLoadStream::commit 函數,后者調用 Copy.transform 進行拷貝,然后調用
Volta884ThreadblockMultiplicandStoreIterator::store_post_increment 保存到 Shared Memory。
Volta884GemmTraits::shared_store_fence 同步 threadblock 內的線程。
GlobalLoadStreamPair::rollback 調用 MMAGlobalLoadStream::rollback 函數,后者調用 TileLoadIterator::initialize_predicates 初始化預測向量,然后移動偏移。

    // Deal with residue in prolog.// global_to_shared_stream.move_to_residue(params.problem_size[0], Traits::OutputTile::kD);global_to_shared_stream.move_to_residue(bounds[0], Traits::OutputTile::kD);// Fetch the fragments for A and B from global memory.global_to_shared_stream.copy();// Copy the elements to shared memory (after transformation if needed).global_to_shared_stream.commit();// Make sure the data is in shared memory.Traits::shared_store_fence(false);// Rollback to the beginning of the first tile (if residue exists).// global_to_shared_stream.rollback(params.problem_size[0] % Traits::OutputTile::kD);global_to_shared_stream.rollback(bounds[0] % Traits::OutputTile::kD);

shared_load_stream為 Volta884GemmTraits::SharedStream 類型,即 SharedStreamPair。
SharedStreamPair::copy 調用 MMASharedLoadStream::copy,后者調用 Volta884WarpMultiplicandLoadIterator::load 從 Shared Memory 加載。
accumulators為 Volta884MultiplyAdd::Accumulators 類型,即 Fragment。
ClearAccumulators::clear 調用 Fragment::clear 將存儲清零。
outer_k是什么?

    // The stream of data from shared memory to fragments.typename Traits::SharedStream shared_load_stream(params.shared_stream,shared_storage.main_loop.threadblock_tile.reference());// Trigger the copy from shared memory for the 1st stream.shared_load_stream.copy(0);// Allocate the accumulators.typename MultiplyAdd::Accumulators accumulators;// Clear the accumulators.clear.clear(accumulators);// Initial index// Index outer_k = params.problem_size[0] - Traits::OutputTile::kD;// problem_size[0] might be bigger than bounds[0]Index outer_k = bounds[0] - Traits::OutputTile::kD;

如果在序幕中計算了剩余,則僅最后一次處理余數。
GemmMainloop::consume_tile 計算k = Traits::OutputTile::kD的分塊。

    // Check if we are computing residue in prolog or not.if (Traits::GemmConfig::kResidueInProlog) {// Execute all mainloop iterations but the last one.CUTLASS_GEMM_LOOPfor (; outer_k > 0; outer_k -= Traits::OutputTile::kD) {CUTLASS_GEMM_LOOP_HEADERconsume_tile<false, false>(global_to_shared_stream, shared_load_stream, accumulators, outer_k);}consume_tile<false, true>(global_to_shared_stream, shared_load_stream, accumulators, outer_k);

否則,每次迭代都考慮余數。

    } else {// When kResidueSeparate = true, execute all mainloop iterations but the last two without any// consideration for K-residue or predicate updates. This improves the steady state of some// kernels.if (Traits::GemmConfig::kResidueSeparate) {CUTLASS_GEMM_LOOPfor (; outer_k > Traits::OutputTile::kD; outer_k -= Traits::OutputTile::kD) {CUTLASS_GEMM_LOOP_HEADERconsume_tile<false, false>(global_to_shared_stream, shared_load_stream, accumulators, outer_k);}}// Execute remaining tiles with K-residue predicate updates enabled.CUTLASS_GEMM_LOOPfor (; outer_k > -Traits::OutputTile::kD; outer_k -= Traits::OutputTile::kD) {CUTLASS_GEMM_LOOP_HEADERconsume_tile<true, false>(global_to_shared_stream, shared_load_stream, accumulators, outer_k);}}

創建 MMAEpilogue 對象,然后調用 MMAEpilogue::epilogue 函數。

    typedef typename Traits::Epilogue Epilogue;Epilogue epilogue(params.epilogue, shared_storage.epilogue, params.problem_size.knm());epilogue.epilogue(accumulators, threadblock_offset, block_swizzle.get_batch_id());}
};

參考資料:

  • # [DOC] Where does cutlass’ detailed GEMM kernel? #526
  • Dissecting the NVIDIA Volta GPU Architecture via Microbenchmarking
  • Modeling Deep Learning Accelerator Enabled GPUs
  • gpgpu-sim_distribution
  • 理解Tensor Core
  • Flexible Performant GEMM Kernels on GPUs
  • CUDA Tensor Core編程
  • PROGRAMMING TENSOR CORES: NATIVE VOLTA TENSOR CORES WITH CUTLASS
  • The NVIDIA Titan V Deep Learning Deep Dive: It’s All About The Tensor Cores
  • 9.7.13.4.1. Matrix Fragments for mma.m8n8k4 with .f16 floating point type
  • Numerical Behavior of NVIDIA Tensor Cores
  • CUDA Ampere Tensor Core HGEMM 矩陣乘法優化筆記 —— Up To 131 TFLOPS!
  • If we have two or four memory requests by a warp, do they need coalesced access/contiguity? #328
  • Do bank conflicts increase when using more shared memory?
  • How does parameter computeType affect the computation?
  • 2.1.10. GEMM Algorithms Numerical Behavior
  • cuBLAS的使用
  • RAFT在Knowhere上的一些評估測試[1]
  • How does parameter computeType affect the computation?
  • cudnn-frontend/tree/main/samples/samples/conv_sample.cpp
  • Is a union in C++ actually a class?
  • A Generalized Micro-kernel Abstraction for GPU Linear Algebra
  • Implementing Strassen’s Algorithm with CUTLASS on NVIDIA Volta GPUs
  • Double-buffering in shared memory, details? #227
  • Efficient GEMM in CUDA
  • Thread synchronization with syncwarp
  • Using CUDA Warp-Level Primitives
  • CUDA微架構與指令集(3)-SASS指令集分類
  • VOLTA Architecture and performance optimization
  • How to Optimize a CUDA Matmul Kernel for cuBLAS-like Performance: a Worklog
  • Determining registers holding the data after executing LDG.E.128
  • 劉冰、鄭鵬|GPU編程和優化-最佳實踐分享

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/160496.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160496.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160496.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

生產問題 Recv-Q101

生產上服務端口 Recv-Q101 新請求到服務器的失敗&#xff0c;幸好及時發現&#xff0c;通過重啟服務之后得到解決&#xff0c;具體原因等待排查 目前覺得的原因是&#xff1a;某些請求暫用時間比較久

Linux超簡單部署個人博客

1 安裝halo 1.1 切換到超級用戶 sudo -i 1.2 新建halo文件夾 mkdir ~/halo && cd ~/halo 1.3 編輯docker-compose.yml文件 vim ~/halo/docker-compose.yml 英文輸入法下&#xff0c;按 i version: "3"services:halo:image: halohub/halo:2.10container_…

2017年全國碩士研究生入學統一考試管理類專業學位聯考數學試題——解析版

文章目錄 2017 級考研管理類聯考數學真題解析一、問題求解&#xff08;本大題共 5 小題&#xff0c;每小題 3 分&#xff0c;共 45 分&#xff09;下列每題給出 5 個選項中&#xff0c;只有一個是符合要求的&#xff0c;請在答題卡上將所選擇的字母涂黑。真題&#xff08;2017-…

Python 提高篇學習筆記(一):深拷貝和淺拷貝

文章目錄 一、什么是對象的引用二、深拷貝和淺拷貝2.1 淺拷貝(Shallow Copy)2.2 深拷貝(Deep Copy)2.3 copy.copy和copy.deepcopy的區別 一、什么是對象的引用 在 Python 中&#xff0c;對象的引用是指變量指向內存中某個對象的地址或標識符。當你創建一個新的對象(比如一個整…

[技術雜談]計算機系統硬件類名稱

在各種編程語言都可以見到利用WMI查詢計算機硬件信息&#xff0c;因此知道有哪些計算機硬件名稱非常有必要&#xff0c;下面列舉了所有計算機硬件名稱可以查詢。 本文內容 冷卻設備類輸入設備類大容量存儲類主板、控制器和端口類 顯示另外 6 個 計算機系統硬件類別將表示硬…

git修改遠程分支名稱

先拉取old_branch最新代碼到本地 git checkout old_branchgit pull origin old_branch本地修改后并推送 git branch -m old_branch new_branch # 修改分支名稱git push --delete origin old_branch # 刪除在遠程的老分支推送新分支 git push origin new_branch本地分支與遠…

除自身以外數組的乘積[中等]

優質博文&#xff1a;IT-BLOG-CN 一、題目 給你一個整數數組nums&#xff0c;返回數組answer&#xff0c;其中answer[i]等于nums中除nums[i]之外其余各元素的乘積。題目數據保證數組nums之中任意元素的全部前綴元素和后綴的乘積都在32位整數范圍內。請不要使用除法&#xff0…

【Qt開發流程】之富文本處理

描述 Scribe框架提供了一組類&#xff0c;用于讀取和操作結構化的富文本文檔。與Qt中以前的富文本支持不同&#xff0c;新的類集中在QTextDocument類上&#xff0c;而不是原始文本信息。這使開發者能夠創建和修改結構化的富文本文檔&#xff0c;而不必準備中間標記格式的內容。…

【數據結構】A : A DS圖_傳遞信息

A : A DS圖_傳遞信息 Description 小明在和他的小伙伴們玩傳消息游戲&#xff0c;游戲規則如下&#xff1a; 有n名玩家&#xff0c;所有玩家編號分別為0~n-1&#xff0c;其中小明編號為0&#xff1b;每個玩家都有固定的若干個可傳信息的其他玩家(也可能沒有)。傳消息的關系是…

busybox制作根文件系統2

上篇內容使用busybox制作好了根文件系統&#xff0c;接下來需要進行一些測試和功能的完善&#xff01; 根文件系統的測試 測試根文件系統的時候不是直接燒寫到EMMC里面&#xff0c;這樣測試效率太低了&#xff0c;Ubuntu的rootfs目錄已經保存了根文件系統&#xff0c;只需要在…

向量數據庫,展望AGI時代

無論是向量數據庫&#xff0c;還是大模型&#xff0c;歸根結底&#xff0c;大家在追捧它時的心態&#xff0c;焦慮大于需求。 向量數據庫的熱潮&#xff0c;在一定程度上“外化”了人們的焦慮。 但這并不能否定向量數據庫的實際價值&#xff0c;甚至更長遠來看&#xff0c;向…

【C++】linux下的gdb程序調試

目錄 【C】Linux 下的 GDB 程序調試1. 安裝 GDB2. 編譯程序3. 啟動 GDB4. 設置斷點5. 執行程序6. 調試命令7. 調試崩潰8. 結束調試 【C】Linux 下的 GDB 程序調試 在開發 C 程序時&#xff0c;出現 bug 是常見的。調試是找出程序錯誤的關鍵步驟之一。在 Linux 環境下&#xff…

RedisTemplate使用詳解

RedisTemplate介紹StringRedisTemplate介紹RedisConnectionFactory介紹RedisConnectionFactory源碼解析 RedisOperations介紹RedisOperations源碼解析 RedisTemplate使用連接池配置RedisTemplate連接池連接池配置 RedisTemplate應用場景RedisTemplate主要特點RedisTemplate使用…

redis運維(十六) 有序集合

一 有序集合 把握一點&#xff1a; 各種redis 命令都提供各種語言對應的API 接口,后續API是關鍵 ① 概念 1、sorted set --> 有序集合2、redis有序集合也是集合類型的一部分&#xff0c;所以它保留了集合中元素不能重復的特性3、但是不同的是,有序集合給每個元素多設置…

什么是數字孿生?

數字孿生是指通過數字化技術手段&#xff0c;將現實世界中的實體物理系統或過程與其數字化模型相連接&#xff0c;實現實體物理系統或過程的虛擬仿真、監測、預測和優化等功能的一種技術。數字孿生技術可以將物理系統的運行狀態、性能參數、故障信息等實時反饋到數字模型中&…

轉型做視頻了,博客就是稿子,繼續堅持寫博客,同時發布視頻,能寫博客說明思路清晰了,能再講明白,理解就更透徹了,緊跟上時代發展。

1&#xff0c;今天特別記錄下&#xff0c;B站給開通了《合集》功能 最近使用視頻制作了幾個視頻。播放量還不錯&#xff0c;最好的已經到了 2.6K了。 然后粉絲也漲到了 200個。 添加鏈接描述 緊跟時代&#xff1a;從寫博客到錄視頻&#xff0c;粉絲大漲&#xff0c;突破200個&…

vue開發一、在Vue中引入ElementUI二、在Vue中使用阿里圖標庫

目錄 一、在Vue中引入ElementUI1. 安裝ElementUI2. 引入ElementUI3. 使用ElementUI組件 二、在Vue中使用阿里圖標庫1. 在阿里圖標庫中選擇圖標2. 下載圖標3. 引入圖標4. 使用圖標 總結 一、在Vue中引入ElementUI ElementUI是一種基于Vue的第三方UI庫&#xff0c;提供了許多常用…

接口自動化測試 —— 工具、請求與響應

一、工具&#xff1a; 1.工具介紹 postman &#xff1a;很主流的API測試工具&#xff0c;也是工作里面使用最廣泛的研發工具。 JMeter&#xff1a; ApiPost&#xff1a; 2.安裝postman&#xff1a; 安裝好直接打開&#xff0c;不用注冊。 二、通信模式&#xff1a; 1、…

【Java 進階篇】從Java對象到JSON:Jackson的魔法之旅

在現代的軟件開發中&#xff0c;處理數據的能力是至關重要的。而當我們談及數據格式時&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;通常是首選。為了在Java中輕松地將對象轉換為JSON&#xff0c;我們需要一種強大而靈活的工具。這時&#xff0c;Jackso…

【Java 進階篇】Redis:打開緩存之門

介紹 Redis&#xff08;Remote Dictionary Server&#xff09;是一個高性能的鍵值對存儲系統&#xff0c;被廣泛用作緩存、消息中間件和數據庫。它以其快速的讀寫能力、支持多種數據結構和豐富的功能而聞名。在這篇博客中&#xff0c;我們將深入了解Redis的概念、安裝以及基本…