tests 10 times ok

This commit is contained in:
zzy 2025-04-14 19:13:46 +08:00
parent 7e6ffdbbd1
commit 073d8283ac
3 changed files with 355 additions and 114 deletions

View File

@ -1,95 +1,239 @@
package mr package mr
import ( import (
"errors"
"fmt" "fmt"
"log" "log"
"net" "net"
"net/http" "net/http"
"net/rpc" "net/rpc"
"os" "os"
"sync"
"time"
) )
const machineDefaultLastSecond = 3
type Coordinator struct { type Coordinator struct {
// Your definitions here. // Your definitions here.
mapTask []MapTask mapTask []MapTask
recudeTask []ReduceTask mapSize int
reduceTask []ReduceTask
reduceSize int
mapTaskQueue chan int taskQueue chan Task
reduceTaskQueue chan int
running chan bool
lock chan bool lock sync.Mutex
machine map[int]*Machine
machineId int
running chan bool
}
func (c *Coordinator) popTask(machine *Machine) {
if machine.Task.TaskType == TASK_NONE {
return
}
DPrintf("machine %d pop task %d, task list is %v %v\n", machine.Id,
machine.Task.TaskId, machine.Task.MapTask, machine.Task.ReduceTask)
c.taskQueue <- machine.Task
machine.Task.TaskType = TASK_NONE
machine.State = MACHINE_IDLE
}
func (c *Coordinator) getMachine(id int) *Machine {
machine, ok := c.machine[id]
if !ok {
DPrintf("machine not found at %d\n", id)
return nil
}
return machine
} }
// Your code here -- RPC handlers for the worker to call. // Your code here -- RPC handlers for the worker to call.
func (c *Coordinator) GetJob(args *Empty, reply *Task) error { func (c *Coordinator) GetTask(args *Machine, reply *Machine) error {
select { c.lock.Lock()
case id := <-c.mapTaskQueue: defer c.lock.Unlock()
reply.MapTask = &c.mapTask[id]
reply.MapTask.TaskId = id
reply.MapTask.State = PROGRESS
reply.TaskType = TASK_MAP if c.Done() {
case id := <-c.reduceTaskQueue: reply.State = MACHINE_DONE
reply.ReduceTask = &c.recudeTask[id] return nil
reply.ReduceTask.TaskId = id
reply.ReduceTask.State = PROGRESS
entries, err := os.ReadDir(".")
if err != nil {
log.Fatal("read dir error")
}
var mapId, reduceId int
for _, entry := range entries {
n, _ := fmt.Sscanf(entry.Name(), "mr-%d-%d", &mapId, &reduceId)
if n == 2 && reduceId == reply.ReduceTask.TaskId {
// log.Printf("reduce task %d find file %s", reply.ReduceTask.TaskId, entry.Name())
reply.ReduceTask.Fnames = append(reply.ReduceTask.Fnames, entry.Name())
}
}
reply.TaskType = TASK_REDUCE
} }
machine := c.getMachine(args.Id)
if machine == nil {
reply.State = MACHINE_INIT
return nil
}
if machine.Task.TaskType != TASK_NONE {
return errors.New("task is not none")
}
c.lock.Unlock()
task, ok := <-c.taskQueue
c.lock.Lock()
if !ok {
reply.State = MACHINE_DONE
return nil
}
machine.Task = task
machine.State = MACHINE_READY
DPrintf("machine %d get task %d, task list is %v %v\n", machine.Id,
machine.Task.TaskId, machine.Task.MapTask, machine.Task.ReduceTask)
*reply = *machine
return nil return nil
} }
func (c *Coordinator) SubmitJob(args *Task, reply *Empty) error { func (c *Coordinator) genReduceTask() {
<-c.lock var mapId, reduceId int
defer func() { c.lock <- true }() entries, err := os.ReadDir(".")
if err != nil {
log.Fatal("read dir error")
}
for _, entry := range entries {
n, _ := fmt.Sscanf(entry.Name(), "mr-%d-%d", &mapId, &reduceId)
if n == 2 && reduceId < c.reduceSize && mapId >= 0 {
// log.Printf("reduce task %d find file %s", reply.ReduceTask.TaskId, entry.Name())
c.reduceTask[reduceId].Fnames = append(c.reduceTask[reduceId].Fnames, entry.Name())
}
}
}
switch args.TaskType { func (c *Coordinator) SubmitTask(args *Machine, reply *Machine) error {
c.lock.Lock()
defer c.lock.Unlock()
if c.Done() {
reply.State = MACHINE_DONE
return nil
}
machine := c.getMachine(args.Id)
if machine == nil {
reply.State = MACHINE_INIT
return nil
}
task := machine.Task
switch task.TaskType {
case TASK_MAP: case TASK_MAP:
// log.Print("submit map task") // log.Printf("submit map task %d\n", task.TaskId)
c.mapTask[args.MapTask.TaskId].State = COMPELETED c.mapTask[task.TaskId].done = true
for _, task := range c.mapTask { for _, task := range c.mapTask {
if task.State != COMPELETED { if !task.done {
return nil goto END
} }
} }
log.Print("start reduce task") c.genReduceTask()
for _, task := range c.recudeTask { DPrintf("end map task\n")
c.reduceTaskQueue <- task.TaskId go func() {
} for i, task := range c.reduceTask {
c.taskQueue <- Task{
MachineId: -1,
TaskId: i,
TaskType: TASK_REDUCE,
ReduceTask: &task,
MapTask: nil,
}
}
}()
// log.Printf("task queue is %+v %+v\n", c.reduceTask, c.taskQueue)
case TASK_REDUCE: case TASK_REDUCE:
// log.Print("submit reduce task") // log.Print("submit reduce task")
c.recudeTask[args.ReduceTask.TaskId].State = COMPELETED c.reduceTask[task.TaskId].done = true
for _, task := range c.recudeTask { for _, task := range c.reduceTask {
if task.State != COMPELETED { if !task.done {
return nil goto END
} }
} }
log.Print("end") DPrintf("end reduce task\n")
// close(c.reduceTaskQueue) for _, m := range c.machine {
// close(c.mapTaskQueue) delete(c.machine, m.Id)
}
close(c.taskQueue)
<-c.running <-c.running
c.running <- false c.running <- false
case TASK_NONE:
log.Print("submit tasknone")
} }
END:
machine.State = MACHINE_IDLE
machine.Task.TaskType = TASK_NONE
*reply = *machine
return nil return nil
} }
func (c *Coordinator) FlushTimeout(args int, reply *Empty) error {
c.lock.Lock()
defer c.lock.Unlock()
if c.Done() {
return nil
}
machine := c.getMachine(args)
if machine == nil {
return errors.New("flush machine not found")
}
machine.LastSecond = machineDefaultLastSecond
return nil
}
func (c *Coordinator) UpdateState(args *Machine, reply *Machine) error {
c.lock.Lock()
defer c.lock.Unlock()
if args.State == MACHINE_INIT {
newId := c.machineId
c.machineId += 1
machine := MakeMachine(newId, machineDefaultLastSecond)
machine.State = MACHINE_IDLE
*reply = *machine
c.machine[newId] = machine
// log.Printf("machine %d init map is %v\n", newId, c.machine)
return nil
}
machine, ok := c.machine[args.Id]
if !ok {
*reply = *args
reply.State = MACHINE_INIT
return nil
}
machine.LastSecond = machineDefaultLastSecond
*reply = *machine
return nil
}
func (c *Coordinator) deamon() {
for {
time.Sleep(time.Second)
c.lock.Lock()
for _, m := range c.machine {
if m.LastSecond > 0 {
m.LastSecond -= 1
}
if m.LastSecond == 0 {
new_m := Machine{}
new_m = *m
go c.popTask(&new_m)
DPrintf("delete machine %d\n", m.Id)
delete(c.machine, m.Id)
}
}
c.lock.Unlock()
}
}
// an example RPC handler. // an example RPC handler.
// //
// the RPC argument and reply types are defined in rpc.go. // the RPC argument and reply types are defined in rpc.go.
@ -130,40 +274,51 @@ func (c *Coordinator) Done() bool {
// nReduce is the number of reduce tasks to use. // nReduce is the number of reduce tasks to use.
func MakeCoordinator(files []string, nReduce int) *Coordinator { func MakeCoordinator(files []string, nReduce int) *Coordinator {
c := Coordinator{ c := Coordinator{
mapTask: make([]MapTask, len(files)), mapTask: make([]MapTask, len(files)),
recudeTask: make([]ReduceTask, nReduce), mapSize: len(files),
mapTaskQueue: make(chan int, len(files)),
reduceTaskQueue: make(chan int, nReduce), reduceTask: make([]ReduceTask, nReduce),
running: make(chan bool, 1), reduceSize: nReduce,
lock: make(chan bool, 1),
taskQueue: make(chan Task),
machine: make(map[int]*Machine),
machineId: 1,
lock: sync.Mutex{},
running: make(chan bool, 1),
} }
// Your code here. // Your code here.
c.running <- true c.running <- true
c.lock <- true
for i, file := range files { for i, file := range files {
c.mapTask[i] = MapTask{ c.mapTask[i] = MapTask{
Fname: file, Fname: file,
State: IDLE, NReduce: nReduce,
NReduce: nReduce, done: false,
TaskId: i,
MachineId: -1,
} }
} }
for i := range nReduce { for i := range nReduce {
c.recudeTask[i] = ReduceTask{ c.reduceTask[i] = ReduceTask{
Fnames: make([]string, 0), Fnames: make([]string, 0),
State: IDLE, done: false,
TaskId: i,
MachineId: -1,
} }
} }
for _, task := range c.mapTask { go func() {
c.mapTaskQueue <- task.TaskId for i, task := range c.mapTask {
} c.taskQueue <- Task{
MachineId: -1,
TaskType: TASK_MAP,
MapTask: &task,
ReduceTask: nil,
TaskId: i,
}
}
}()
go c.deamon()
c.server() c.server()
return &c return &c

View File

@ -7,6 +7,7 @@ package mr
// //
import ( import (
"fmt"
"os" "os"
"strconv" "strconv"
) )
@ -26,42 +27,71 @@ type ExampleReply struct {
// Add your RPC definitions here. // Add your RPC definitions here.
type State int func DPrintf(format string, a ...any) (int, error) {
const Debug = false
var n int
var err error
if Debug {
n, err = fmt.Printf(format, a...)
}
return n, err
}
type MachineState int
const ( const (
IDLE State = iota MACHINE_INIT MachineState = iota
PROGRESS MACHINE_IDLE
COMPELETED MACHINE_READY
MACHINE_RUNNING
MACHINE_DONE
) )
type Machine struct {
State MachineState
Id int
LastSecond int
Task Task
}
func MakeMachine(id int, lastSecond int) *Machine {
return &Machine{
State: MACHINE_INIT,
Id: id,
LastSecond: lastSecond,
Task: Task{
TaskType: TASK_NONE,
MapTask: nil,
ReduceTask: nil,
},
}
}
type TaskType int type TaskType int
const ( const (
TASK_IDLE TaskType = iota TASK_NONE TaskType = iota
TASK_MAP TASK_MAP
TASK_REDUCE TASK_REDUCE
TASK_DONE
) )
type Task struct { type Task struct {
TaskId int
MachineId int
TaskType TaskType TaskType TaskType
MapTask *MapTask MapTask *MapTask
ReduceTask *ReduceTask ReduceTask *ReduceTask
} }
type MapTask struct { type MapTask struct {
Fname string Fname string
State State NReduce int
NReduce int done bool
TaskId int
MachineId int
} }
type ReduceTask struct { type ReduceTask struct {
Fnames []string Fnames []string
State State done bool
MachineId int
TaskId int
} }
type Empty struct { type Empty struct {

View File

@ -9,6 +9,8 @@ import (
"net/rpc" "net/rpc"
"os" "os"
"sort" "sort"
"sync/atomic"
"time"
) )
// Map functions return a slice of KeyValue. // Map functions return a slice of KeyValue.
@ -34,32 +36,84 @@ func Worker(mapf func(string, string) []KeyValue,
// uncomment to send the Example RPC to the coordinator. // uncomment to send the Example RPC to the coordinator.
// CallExample() // CallExample()
empty := Empty{} machine := MakeMachine(-1, -1)
for { var id atomic.Int32
reply := Task{}
ok := call("Coordinator.GetJob", &empty, &reply)
if !ok {
break
}
switch reply.TaskType { for {
case TASK_MAP: reply := Machine{}
kva := workMap(mapf, reply.MapTask) state := machine.State
saveMap(kva, reply.MapTask) switch state {
call("Coordinator.SubmitJob", &reply, &empty) case MACHINE_INIT:
case TASK_REDUCE: // log.Printf("worker init %d\n", machine.Id)
workRecude(reducef, reply.ReduceTask) if ok := call("Coordinator.UpdateState", machine, &reply); !ok {
call("Coordinator.SubmitJob", &reply, &empty) reply.State = MACHINE_DONE
case TASK_IDLE: } else {
case TASK_DONE: id.Store(int32(reply.Id))
goto END go func() {
for {
time.Sleep(time.Second)
rid := id.Load()
if ok := call("Coordinator.FlushTimeout", rid, &Empty{}); !ok {
log.Printf("FlushTimeout error %v\n", ok)
}
}
}()
}
machine = &reply
case MACHINE_READY:
if ok := runOnceTask(machine, mapf, reducef); !ok {
machine.State = MACHINE_IDLE
}
case MACHINE_IDLE:
// log.Printf("worker idle %d\n", machine.Id)
machine.Task.TaskType = TASK_NONE
if ok := call("Coordinator.GetTask", machine, &reply); !ok {
reply.State = MACHINE_DONE
}
machine = &reply
case MACHINE_DONE:
return
default:
goto SLEEP
} }
continue
SLEEP:
time.Sleep(time.Second)
} }
END:
} }
func workMap(mapf func(string, string) []KeyValue, params *MapTask) []KeyValue { func runOnceTask(machine *Machine,
filename := params.Fname mapf func(string, string) []KeyValue,
reducef func(string, []string) string) bool {
machine.State = MACHINE_RUNNING
task := &machine.Task
switch task.TaskType {
case TASK_MAP:
kva := workMap(mapf, task)
saved_fname := saveMap(kva, task)
if saved_fname == nil {
return false
}
case TASK_REDUCE:
workRecude(reducef, task)
default:
log.Printf("unknown task type %d", task.TaskType)
return false
}
// log.Printf("submit task %d %+v %+v", task.TaskId, task.MapTask, task.ReduceTask)
reply := Machine{}
if ok := call("Coordinator.SubmitTask", machine, &reply); !ok {
return false
}
*machine = reply
return true
}
func workMap(mapf func(string, string) []KeyValue, params *Task) []KeyValue {
filename := params.MapTask.Fname
file, err := os.Open(filename) file, err := os.Open(filename)
if err != nil { if err != nil {
@ -74,10 +128,11 @@ func workMap(mapf func(string, string) []KeyValue, params *MapTask) []KeyValue {
return kva return kva
} }
func saveMap(kva []KeyValue, params *MapTask) []string { func saveMap(kva []KeyValue, params *Task) []string {
fnames := make([]string, params.NReduce) nReduce := params.MapTask.NReduce
files := make([]*os.File, params.NReduce) fnames := make([]string, nReduce)
encodes := make([]*json.Encoder, params.NReduce) files := make([]*os.File, nReduce)
encodes := make([]*json.Encoder, nReduce)
for i := range fnames { for i := range fnames {
fnames[i] = fmt.Sprintf("mr-%d-%d", params.TaskId, i) fnames[i] = fmt.Sprintf("mr-%d-%d", params.TaskId, i)
@ -99,7 +154,7 @@ func saveMap(kva []KeyValue, params *MapTask) []string {
}() }()
for _, kv := range kva { for _, kv := range kva {
enc := encodes[ihash(kv.Key)%params.NReduce] enc := encodes[ihash(kv.Key)%nReduce]
if err := enc.Encode(&kv); err != nil { if err := enc.Encode(&kv); err != nil {
log.Fatalf("cannot encode %v", kv) log.Fatalf("cannot encode %v", kv)
break break
@ -117,11 +172,12 @@ func (a ByKey) Len() int { return len(a) }
func (a ByKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByKey) Less(i, j int) bool { return a[i].Key < a[j].Key } func (a ByKey) Less(i, j int) bool { return a[i].Key < a[j].Key }
func workRecude(reducef func(string, []string) string, params *ReduceTask) { func workRecude(reducef func(string, []string) string, params *Task) {
reduceId := params.TaskId reduceId := params.TaskId
fnames := params.ReduceTask.Fnames
intermediate := []KeyValue{} intermediate := []KeyValue{}
for _, fname := range params.Fnames { for _, fname := range fnames {
file, err := os.Open(fname) file, err := os.Open(fname)
if err != nil { if err != nil {
log.Fatal("can't open file") log.Fatal("can't open file")