MIT 6.5840 (Spring, 2024) 通關指南——Lab 1: MapReduce

MIT 6.5840 (Spring, 2024) – Lab 1: MapReduce

👨?💻 Charles

🔗 實驗手冊: 6.5840 Lab 1: MapReduce

📃 MapReduce 論文原文: mapreduce-osdi04.pdf

?? 本系列前文: MIT 6.5840 (Spring, 2024) 通關指南——入門篇

文章目錄

  • MIT 6.5840 (Spring, 2024) -- Lab 1: MapReduce
    • 代碼理解
      • baseline:串行實現
      • todo:并行實現
        • `coordinator.go`
        • `worker.go`
    • 代碼實現
      • `coordinator.go`
        • 初始化
        • 處理 Map 任務
        • 處理 Reduce 任務
        • 監控任務情況(防超時)
      • `rpc.go`
        • worker 獲取 Map 任務
        • worker 提交 Map 結果
        • worker 獲取 Reduce 任務
        • worker 提交 Reduce 結果
      • `worker.go`
        • 初始化
        • 處理 Map 任務
        • 處理 Reduce 任務
    • 實驗結果
    • 踩坑記錄/建議

代碼理解

baseline:串行實現

首先,看看 Lab 中已給出的一個串行版 MapReduce —— src/main/mrsequential.go ,這是我們后續自己實現并行版本的重要參考。在 mrsequential.go 中,有 mapfreducef 兩個組件,分別對應 Map 任務和 Reduce 任務:

mapf, reducef := loadPlugin(os.Args[1])

可以看到,它們是通過插件的形式導入的, loadPlugin 的實現在 mrsequential.go 中,利用了golang的 plugin 庫,所以我們可以看到實驗手冊運行 mrsequential.go 之前先運行了:

go build -buildmode=plugin ../mrapps/wc.go

即將 wc.go 編譯為 wc.so (動態加載共享庫),之后運行 mrsequential.go 的時候就可以這樣使用 wc.go 中的各種方法:

go run mrsequential.go wc.so pg*.txt

Anyway,其實就是說,Map 和 Reduce 的實現要到 src/mrapps/wc.go 中去找。源代碼也挺簡單的,實現方法為:

  • Split:以非字母符號為分隔符,將輸入文件拆分為若干個單詞,存到切片 words

  • Map:順序處理 words 的單詞,對于每個單詞 w ,構建一個鍵值對 {w, 1} ,將這個鍵值對存到一個切片 kva

  • Reduce:統計 kva 中,每個單詞的個數(在 kva 排序后,相同的單詞挨在一起,把它們放到新切片 values := []string{} 中,Reduce其實就是返回 len(values)

todo:并行實現

本實驗主要需要在已提供的代碼基礎上,完善 mr/coordinator.gomr/worker.gomr/rpc.go 。為了實現單 coordinator、多 worker 的并行架構,coordinator 需要負責給各 worker 分配 Map 任務和 Reduce 任務,并監控 worker 的工作情況、在發生超時的時候將其任務重新分配給其他 worker;同時,每個 worker 需要通過 RPC 調用 coordinator 的 Map 方法和 Reduce 方法,并保存相關結果、告知 coordinator 完成情況。

coordinator.go

我們需要實現的并行版 MapReduce 的主程序在 src/main/mrcoordinator.go 中,它負責調用 MakeCoordinator 構建 coordinator (任務分發者,相當于server)——這是在 src/mr/coordinator.go 中實現的,這個文件中已經聲明/提示了我們 需要補全 的若干方法(見注釋)。

coordinator 啟動后,會通過 server() 方法創建一個 goroutine 來監聽 src/mr/worker.goRPC 調用請求:

//
// start a thread that listens for RPCs from worker.go
//
func (c *Coordinator) server() {rpc.Register(c)rpc.HandleHTTP()//l, e := net.Listen("tcp", ":1234")sockname := coordinatorSock()os.Remove(sockname)l, e := net.Listen("unix", sockname)if e != nil {log.Fatal("listen error:", e)}go http.Serve(l, nil)
}

關于RPC的使用方法, worker.gocoordinator.go 中都有示例函數,所用的相關參數/方法定義在 src/mr/rpc.go 中。

建議先在現有代碼上嘗試 RPC 調用示例函數( worker.go 里面有個 CallExample() 基本可以直接用),從而熟悉代碼框架。

關于 RPC 以及 golang 中如何使用 RPC,建議逢山開路,遇到不懂的就問 AI 🤖

worker.go

worker.go 即 map 和 reduce 任務的執行者,需要補全 Worker 方法:

// main/mrworker.go calls this function.
func Worker(mapf func(string, string) []KeyValue,reducef func(string, []string) string) {...}

關鍵在于處理和 coordinator 的通信(需要通過 RPC 調用,獲取任務、執行任務)。

代碼實現

完整代碼: MIT-6.5840/src/mr at lab1 · Charles-T-T/MIT-6.5840

coordinator.go

Coordinator 結構體定義如下:

type Coordinator struct {mu            sync.RWMutexnMap          intnReduce       inttoMapTasks    chan MapTasktoReduceTasks chan ReduceTaskremainMapTask    map[string]string // filename -> workerIDremainReduceTask map[string]string // reduceID -> workerIDworkerRegistry   map[string]string // workerID -> workerAddrallMapDone       boolallReduceDone    bool
}
  • mu :讀寫鎖,用于防止多個worker訪問同一個coordinator成員出現沖突

  • workerRegistry :記錄已經注冊了的worker——只有已注冊的worker提交的Map或Reduce結果才會被接受(防止收到超時worker的任務結果——已被重新分配了)

    原本設計的是workerID ?? workerAddr(worker 的 sock 地址)的一個哈希表,但是后續實現中發現維護 coordinator 和 worker 的雙向通信似乎沒必要,故這里僅當作一個集合使用。

  • nMapnReduce :需要執行的Map和Reduce任務總數

  • 其余成員變量作用易從其名稱得出

初始化
// create a Coordinator.
// main/mrcoordinator.go calls this function.
// nReduce is the number of reduce tasks to use.
func MakeCoordinator(files []string, nReduce int) *Coordinator {c := Coordinator{nMap:             len(files),nReduce:          nReduce,toMapTasks:       make(chan MapTask, len(files)),toReduceTasks:    make(chan ReduceTask, nReduce),remainMapTask:    make(map[string]string),remainReduceTask: make(map[string]string),workerRegistry:   make(map[string]string),allMapDone:       false,allReduceDone:    false,}...
處理 Map 任務

初始化后,啟動一個 goroutine 來處理 Map 任務:

// Manage Map tasks
go func() {// Init todo Map tasksfor i, file := range files {mapTask := MapTask{Filename: file,MapID:    strconv.Itoa(i),NMap:     c.nMap,NReduce:  c.nReduce,}c.toMapTasks <- mapTaskDPrintf("Get todo-file: %s\n", file)c.remainMapTask[file] = "init"}// Wait all Map tasks to be donefor len(c.remainMapTask) > 0 {time.Sleep(time.Second)}close(c.toMapTasks)c.allMapDone = trueDPrintf("All map tasks done.\n")
}()
處理 Reduce 任務

啟動另一個 goroutine 來處理 Reduce 任務:

// Manage Reduce tasks
go func() {// output files for reduce resultsfor i := 0; i < nReduce; i++ {c.toReduceTasks <- ReduceTask{ReduceID: strconv.Itoa(i)}c.remainReduceTask[strconv.Itoa(i)] = "init"}// Wait all Map tasks to be donefor !c.allMapDone {time.Sleep(time.Second)}// Wait all Reduce tasks to be donefor len(c.remainReduceTask) > 0 {time.Sleep(time.Second)}close(c.toReduceTasks)c.allReduceDone = trueDPrintf("All reduce tasks done.\n")
}()
監控任務情況(防超時)

每次 worker 開始一個任務后,coordinator 就會啟動一個 goroutine ——如果 10s(實驗手冊建議的超時時間)后任務仍未完成則視為超時,需要將該任務放回 todo-channel 中,等待其他 worker 認領:

// Monitor a Map task, reassign it if time out.
func (c *Coordinator) monitorMapTask(file string, mapID string) {time.Sleep(time.Second * 10) // wait for 10sworkerID, exist := c.remainMapTask[file]if exist {c.mu.Lock()delete(c.workerRegistry, workerID)DPrintf("Map job by %s time out!\n", workerID)c.mu.Unlock()c.toMapTasks <- MapTask{Filename: file, MapID: mapID, NMap: c.nMap, NReduce: c.nReduce}}
}// Monitor a Reduce task, reassign it if time out.
func (c *Coordinator) monitorReduceTask(reduceID string) {time.Sleep(time.Second * 10) // wait for 10sworkerID, exist := c.remainReduceTask[reduceID]if exist {c.mu.Lock()delete(c.workerRegistry, workerID)DPrintf("Reduce job by %s time out!\n", workerID)c.mu.Unlock()c.toReduceTasks <- ReduceTask{ReduceID: reduceID}}
}

rpc.go

worker 需要 RPC 調用 coordinator 的各方法均寫在 rpc.go 中。

worker 獲取 Map 任務

每個worker啟動后,會首先嘗試從coordinator的 toMapTasks channel 中獲取一個Map任務,如果所有Map任務已完成、channel已關閉,則返回任務的 AllMapDone 字段為 true ;如果獲取任務成功,則worker在 workerRegistry 注冊,同時coordinator啟動監視( c.monitorMapTask ),以在任務超時后重新分配任務。

func (c *Coordinator) WorkerGetMapTask(workerID string, mapTask *MapTask) error {toMapTask, ok := <-c.toMapTasksif ok {mapTask.Filename = toMapTask.FilenamemapTask.MapID = toMapTask.MapIDmapTask.NReduce = toMapTask.NReduce} else {mapTask.AllMapDone = true // all Map tasks already done.mapTask.AllReduceDone = c.allReduceDonereturn nil}// worker registersc.mu.Lock()c.workerRegistry[workerID] = workerSock(workerID)c.remainMapTask[toMapTask.Filename] = workerIDgo c.monitorMapTask(toMapTask.Filename, toMapTask.MapID)c.mu.Unlock()return nil
}
worker 提交 Map 結果

worker 完成其 Map 任務后,需要告知 coordinator,隨后 coordinator 會從 remainMapTask 中移除該任務,視為任務完成。coordinator 只接受注冊了的 worker 的結果。

worker 具體處理 Map 任務的過程在 worker.go 中,此處只是“通知任務完成”。

func (c *Coordinator) WorkerGiveMapRes(mapTask MapTask, reply *string) error {// Coordinator only accepts results from worker IN workerRegistryworkerID := mapTask.WorkerIDfilename := mapTask.Filename_, exist := c.workerRegistry[workerID]if !exist {DPrintf("Illegal map result: get from unknown worker: %s\n", workerID)return nil}c.mu.Lock()DPrintf("Successfully get map result from: %s\n", workerID)delete(c.remainMapTask, filename)c.mu.Unlock()return nil
}
worker 獲取 Reduce 任務

實現思路和獲取 Map 任務的一致:

func (c *Coordinator) WorkerGetReduceTask(workerID string, reduceTask *ReduceTask) error {toReduceTask, ok := <-c.toReduceTasksif ok {*reduceTask = toReduceTaskreduceTask.WorkerID = workerIDreduceTask.TempResFile = fmt.Sprintf("mr-tmp-%s", workerID)} else {reduceTask.AllReduceDone = true // all Reduce tasks already done.return nil}// worker registersc.mu.Lock()c.workerRegistry[workerID] = workerSock(workerID)c.remainReduceTask[toReduceTask.ReduceID] = workerIDgo c.monitorReduceTask(toReduceTask.ReduceID)c.mu.Unlock()return nil
}
worker 提交 Reduce 結果

實現思路和提交 Map 結果的一致:

func (c *Coordinator) WorkerGiveReduceRes(reduceTask ReduceTask, reply *string) error {// Coordinator only accepts results from worker in workerRegistryworkerID := reduceTask.WorkerID_, exist := c.workerRegistry[workerID]if !exist {DPrintf("Illegal reduce result: get from unknown worker: %s\n", workerID)return nil}newname := fmt.Sprintf("mr-out-%s", reduceTask.ReduceID)*reply = newnameerr := os.Rename(reduceTask.TempResFile, newname)if err != nil {DPrintf("Error when rename temp file: %v\n", err)}c.mu.Lock()DPrintf("Successfully get reduce result from: %s\n", workerID)delete(c.remainReduceTask, reduceTask.ReduceID)c.mu.Unlock()return nil
}

worker.go

worker 采用的 Map 和 Reduce 方法是通過不同插件載入的,我們不需要關心其實現,直接用就行了。

初始化
workerID := strconv.Itoa(os.Getpid())
mapDone := false    // flag whether all Map tasks have been finished
reduceDone := false // flag whether all Reduce tasks have been finished
處理 Map 任務

worker 啟動后,周期性嘗試從 coordinator 那里獲取一個 Map 任務,獲取任務后處理、向 coordinator 提交結果,直到收到所有 Map 任務已完成的通知,則將 mapDone 置為 true

// Do the map task
for !mapDone {mapTask := MapTask{WorkerID: workerID}DPrintf("<%s> ask for a map task...\n", workerID)call("Coordinator.WorkerGetMapTask", workerID, &mapTask)DPrintf("<%s> get task: %s\n", workerID, mapTask.Filename)if !mapTask.AllMapDone {file, err := os.Open(mapTask.Filename)if err != nil {DPrintf("cannot open %v\n", mapTask.Filename)return}content, err := io.ReadAll(file)if err != nil {DPrintf("cannot read %v\n", mapTask.Filename)return}file.Close()kva := mapf(mapTask.Filename, string(content))saveMapRes(kva, mapTask.MapID, mapTask.NReduce)mapTask.Result = kvavar reply stringcall("Coordinator.WorkerGiveMapRes", mapTask, &reply)} else {mapDone = truereduceDone = mapTask.AllReduceDoneDPrintf("All map tasks done.\n")}time.Sleep(500 * time.Millisecond)
}

其中,Map 任務產生的中間結果需要保存到文件中,參考實驗手冊的 hint:

hint1

實現如下:

func saveMapRes(kva []KeyValue, mapID string, nReduce int) {reduceChunks := make(map[string][]KeyValue) // reduceID -> kvsfor _, kv := range kva {reduceID := strconv.Itoa(ihash(kv.Key) % nReduce)reduceChunks[reduceID] = append(reduceChunks[reduceID], kv)}for reduceID, kvs := range reduceChunks {oname := fmt.Sprintf("mr-%s-%s.json", mapID, reduceID)ofile, _ := os.Create(oname)defer ofile.Close()enc := json.NewEncoder(ofile)err := enc.Encode(&kvs)if err != nil {DPrintf("Error when encoding kv: %v\n", err)}}DPrintf("Finish saving map result.\n")
}
處理 Reduce 任務

和處理 Map 任務的思路一致——周期性嘗試獲取一個 Reduce 任務 ?? 處理 Reduce 任務 ?? 保存 Reduce 結果 ?? 向 coordinator 提交結果:

// Do the Reduce task
for !reduceDone {reduceTask := ReduceTask{WorkerID: workerID}DPrintf("<%s> ask for a reduce task...\n", workerID)call("Coordinator.WorkerGetReduceTask", workerID, &reduceTask)DPrintf("<%s> get reduceID: %s\n", workerID, reduceTask.ReduceID)if !reduceTask.AllReduceDone {// Get Map result files to be Reducedpattern := fmt.Sprintf(`^mr-.*-%s.json$`, regexp.QuoteMeta(reduceTask.ReduceID))re := regexp.MustCompile(pattern)files, err := os.ReadDir(".")if err != nil {fmt.Println("Error reading directory:", err)return}var toReduceFiles []stringfor _, file := range files {if !file.IsDir() && re.MatchString(file.Name()) {toReduceFiles = append(toReduceFiles, file.Name())}}// Do the reduce jobdoReduce(toReduceFiles, reducef, reduceTask.TempResFile)DPrintf("<%s> finish reduce job, res to %s.\n", workerID, reduceTask.TempResFile)var reply stringcall("Coordinator.WorkerGiveReduceRes", reduceTask, &reply)DPrintf("<%s> reduce res save to %s.\n", workerID, reply)} else {reduceDone = trueDPrintf("All reduce done.\n")}time.Sleep(100 * time.Millisecond)
}

其中負責執行 Reduce 的方法 doReduce 主要參考 mrsequential.go 實現:

func doReduce(toReduceFiles []string, reducef func(string, []string) string, oname string) {ofile, _ := os.Create(oname)defer ofile.Close()intermediate := []KeyValue{}for _, toReduceFile := range toReduceFiles {file, _ := os.Open(toReduceFile)dec := json.NewDecoder(file)kva := []KeyValue{}if err := dec.Decode(&kva); err != nil {DPrintf("Error when json decode: %v\n", err)return}intermediate = append(intermediate, kva...)file.Close()}sort.Sort(ByKey(intermediate))i := 0for i < len(intermediate) {j := i + 1for j < len(intermediate) && intermediate[j].Key == intermediate[i].Key {j++}values := []string{}for k := i; k < j; k++ {values = append(values, intermediate[k].Value)}output := reducef(intermediate[i].Key, values)fmt.Fprintf(ofile, "%v %v\n", intermediate[i].Key, output)i = j}
}

實驗結果

手動測試并打印中間過程(在 worker.go 中將 Debug 設置為 true ):
lab1-res1

運行測試腳本 mr-test.sh
res2

測試通過。

踩坑記錄/建議

  • DPrintf 打印日志能發現大部分 bug,但是可能有些細節需要用打斷點調試,如果是 vscode 的話需要配置一下:

    • 例如,對于 worker,要斷點調試 wc 任務,需要在 .vscode/launch.json 中添加配置:

      {"name": "mrworker-wc","type": "go","request": "launch","mode": "exec","program": "${workspaceFolder}/6.5840/src/main/mrworker","args": ["wc.so"],"cwd": "${workspaceFolder}/6.5840/src/main"
      },
      
    • 對于 coordinator,可以配置:

      {"name": "debug mrcoordinator","type": "go","request": "launch","mode": "auto","program": "${workspaceFolder}/6.5840/src/main/mrcoordinator.go","args": ["pg-being_ernest.txt","pg-dorian_gray.txt","pg-frankenstein.txt","pg-grimm.txt","pg-huckleberry_finn.txt","pg-metamorphosis.txt","pg-sherlock_holmes.txt","pg-tom_sawyer.txt"]
      }
      

      具體參數可以根據任務調整,不懂的多問 AI。

  • 本 lab 實現的是一個 MapReduce 框架 ,也就是說具體的 Map 任務和 Reduce 任務 不是一定的 ——我一開始以為只有單詞計數( src/mrapps/wc.go ),所以傻了吧唧地搬運 mrsequential.go 的代碼,但實際上最后測試的任務有很多,都在 src/mrapps/ 下。最后跑 test-mr.sh 的時候,也可以根據出錯任務到 src/mrapps/ 中看看對應任務代碼,可能有所啟發。

  • RPC 函數,不僅 函數名首字母大寫 ,如果參數是結構體,則該結構體中的 成員變量也要首字母大寫

    否則你可能會像我一樣,發現 reply 中有些成員被更新了、有些沒有,非常詭異 🤷?♂ ?

  • 仔細閱讀官方實驗手冊的 Hints ,很有用。

    比如前一條,其實 Hints 中就有提到:

    “Go RPC sends only struct fields whose names start with capital letters. Sub-structures must also have capitalized field names.”

  • 注意采用合理方法保存 Map 任務的中間結果,便于之后 Reduce 任務讀取。 Hints 中的建議是:

    “A reasonable naming convention for intermediate files is mr-X-Y, where X is the Map task number, and Y is the reduce task number.”

  • 注意給 coordinator 上鎖,防止多 worker 的讀寫沖突。


如果你覺得有幫助,歡迎去 我的代碼倉庫 點個 star ?? : )

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

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

相關文章

吳恩達機器學習作業五:神經網絡正向傳播

數據集在作業一正向傳播正向傳播&#xff08;Forward Propagation&#xff09;是神經網絡計算過程中的核心步驟&#xff0c;指的是將輸入數據通過神經網絡的各層依次傳遞&#xff0c;最終得到輸出結果的過程。核心原理在神經網絡中&#xff0c;信息從輸入層流入&#xff0c;經過…

網絡編程(4)

【0】復習 sockfdsocket(); //指定網絡信息 bind(); listen(); //創建表 fd_set rfds,tempfds; FD_ZERO(); FD_SET(sockfd); max sockfd while(1) {tempfdsrfds;select(max1,&tempfds)if(FD_ISSET(scokfd,&tempfds)){acceptfdaccept();FD_SET(acceptfd,&rfds);if(m…

Windows系統提示“找不到文件‘javaw‘”

1. Java 未安裝或安裝不完整javaw.exe 是 Java 運行環境&#xff08;JRE&#xff09;的核心文件&#xff0c;用于運行 Java 程序&#xff08;如.jar 文件&#xff09;。如果你的電腦沒有安裝 Java&#xff0c;或安裝過程中 javaw.exe 被誤刪&#xff0c;系統就會找不到它。2. J…

【PCIE系列】1---PCIE系統拓撲結構分析

架構由點對點鏈路&#xff08;Links&#xff09;組成&#xff0c;用于互連組成系統的一系列組件。下圖展示了一個示例拓撲結構。該圖描述了一個有層次的體系架構實例&#xff0c;其包含根復合體&#xff08;Root Complex, RC&#xff09;、多個端點&#xff08;I/O設備&#xf…

SpringBoot防止重復提交(2)

例如&#xff1a;多次點擊提現按鈕問題描述&#xff1a;在提現操作中&#xff0c;用戶可能會多次點擊提現按鈕&#xff0c;導致多個相同的請求發送到服務器&#xff0c;從而引發重復提現的問題。為了解決這一問題&#xff0c;必須保證每個提現請求只能執行一次&#xff0c;防止…

mysql zip包安裝步驟

下載地址 windows MSI Install 安裝包程序。 這里下載zip包&#xff0c;執行安裝過程 確認my.ini 配置的路徑&#xff0c;創建mysql數據服務的data目錄管理員身份cmd 進入bin目錄&#xff0c;開始初始化服務 mysqld --initialize-insecure --usermysql mysqld -install#啟動…

Python 的 argparse 模塊中,add_argument 方法的 nargs 參數

在 Python 的 argparse 模塊中&#xff0c;add_argument 方法的 nargs 參數用于指定命令行參數可以接受的參數數量。你提到的 nargs* 和 nargs 是兩種常見設置&#xff0c;它們分別表示不同的參數數量要求。以下是兩者的詳細區別和含義&#xff1a;1. nargs*: 接受零個或多個參…

嵌入式Linux LED驅動開發

嵌入式Linux LED驅動開發 一、LED驅動概述 本筆記基于IMX6ULL處理器的LED驅動開發&#xff0c;詳細介紹了字符設備驅動開發的基本流程。該驅動實現了對LED的基本控制功能&#xff0c;通過字符設備接口供用戶空間程序調用。 二、LED驅動核心概念 1. 寄存器地址定義 本驅動涉…

Excel Word Pdf 格式轉換

引入aspose包手動更新本地mvn倉庫mvn install:install-file -DfileC:\aspose-cells-22.9.jar -DgroupIdaspose -DartifactIdaspose-cells -Dversion22.9 -Dpackagingjar mvn install:install-file -DfileC:\aspose-pdf-22.9.jar -DgroupIdaspose -DartifactIdaspose-pdf -Dvers…

變頻器實習DAY40 調整測試零伺服PI LDO

目錄變頻器實習DAY40一、工作內容1.1 調整測試零伺服PI二、學習內容2.1 LDOLDO的核心工作原理——“采樣-比較-調整”閉環控制LDO的關鍵參數——選型核心依據LDO與其他穩壓器的選型對比附學習參考網址歡迎大家有問題評論交流 (* ^ ω ^)變頻器實習DAY40 一、工作內容 1.1 調整…

【半導體制造流程概述】

半導體制造流程概述 半導體制造是一個高度復雜且精密的過程&#xff0c;涉及多個關鍵步驟&#xff0c;通常分為以下幾個主要階段&#xff1a;設計、晶圓制備、光刻、刻蝕、摻雜、薄膜沉積、互連和封裝測試。 文章目錄半導體制造流程概述晶圓制備光刻刻蝕摻雜薄膜沉積互連封裝測…

為什么大模型需要文檔預處理:從數據到智能的關鍵一步

在人工智能&#xff0c;尤其是大語言模型&#xff08;LLM, Large Language Models&#xff09;的應用落地過程中&#xff0c;數據質量與處理流程的重要性正逐漸被各行各業所認識。無論是企業內部構建知識庫、自動化文檔審核&#xff0c;還是面向用戶提供智能問答服務&#xff0…

50.【.NET8 實戰--孢子記賬--從單體到微服務--轉向微服務】--新增功能--二期功能規劃

啰嗦了這么多文章&#xff0c;我們終于進入到了二期功能的開發。這篇文章我們先來規劃一下二期要做的功能&#xff0c;在一期功能中&#xff0c;我們完成了基礎的記賬功能&#xff0c;但是作為一個記賬軟件&#xff0c;僅有這些功能是遠遠不夠的。我們需要更多的功能來滿足用戶…

Oracle下載安裝(學習版)

1. 下載&#xff08;學習版&#xff09; 網址&#xff1a;軟件下載 | Oracle 中國 2. 安裝 解壓縮 雙擊可執行文件 下一步 選同意&#xff0c;下一步 下一步 設置密碼&#xff08;自己記住&#xff09; 開始安裝 測試安裝是否成功

`basic_filebuf`、`basic_ifstream`、`basic_ofstream`和 `basic_fstream`。

C 文件 I/O 模板類深度解析 文章目錄C 文件 I/O 模板類深度解析1. basic_filebuf 深度解析1.1 類模板定義詳解1.2 關鍵成員變量1.3 核心成員函數實現原理1.3.1 open() 函數實現1.3.2 overflow() 函數實現1.4 完整示例&#xff1a;自定義緩沖策略2. basic_ifstream 深度解析2.1 …

計算機畢設 java 阿歹果園養雞場管理系統 基于 SSM 框架的果園養雞場全流程管理系統設計與實現 Java+MySQL 的養殖生產與進銷存一體化平臺開發

計算機畢設 java 阿歹果園養雞場管理系統ky7dc9 &#xff08;配套有源碼 程序 mysql數據庫 論文&#xff09;本套源碼可以先看具體功能演示視頻領取&#xff0c;文末有聯xi 可分享 隨著農業養殖規模化發展&#xff0c;傳統果園養雞場依賴人工記錄、紙質臺賬的管理模式&#xf…

生成式BI工具(WrenAI)

生成式 BI 工具支持自然語言查詢數據庫&#xff0c;自動生成 SQL 與可視化圖表&#xff0c;被金融分析師和數據科學家廣泛采用。 WrenAI是由Canner團隊開發的開源生成式BI&#xff08;GenBI&#xff09;智能體&#xff0c;致力于通過自然語言交互實現數據庫查詢、可視化生成和洞…

論文Review 3DGS PGSR | TVCG2024 ZJU-3DV | 幾何約束的3DGS表面重建

基本信息 題目&#xff1a;PGSR: Planar-based Gaussian Splatting for Efficient and High-Fidelity Surface Reconstruction 來源&#xff1a;TVCG2024 學校&#xff1a;ZJU-3DV 是否開源&#xff1a;https://github.com/zju3dv/PGSR 摘要&#xff1a;3DGS表面重建 最近…

最新After Effects2025下載安裝(含安裝包)AE 2025 保姆級下載一鍵安裝圖文教程

文章目錄一、After Effects 2025下載二、After Effects 2025安裝教程三、核心功能升級詳解四、系統配置與兼容性說明一、After Effects 2025下載 ①夸克網盤下載鏈接&#xff1a;https://pan.quark.cn/s/a06e6200e64c 二、After Effects 2025安裝教程 1.解壓安裝包:找到下載…

【網絡安全領域】邊界安全是什么?目前的發展及應用場景

在網絡安全領域&#xff0c;邊界安全&#xff08;Perimeter Security&#xff09; 是指圍繞企業或組織網絡的 “物理與邏輯邊界” 構建的防護體系&#xff0c;核心目標是阻止未授權訪問從外部網絡&#xff08;如互聯網、合作方網絡&#xff09;侵入內部可信網絡&#xff0c;同時…