Bladeren bron

增加样例

小阿七 2 jaren geleden
bovenliggende
commit
708f043a89
7 gewijzigde bestanden met toevoegingen van 367 en 1 verwijderingen
  1. 7 1
      README.md
  2. 90 0
      dag.yaml
  3. 24 0
      dag_test.go
  4. 2 0
      go.mod
  5. 4 0
      go.sum
  6. 98 0
      queue.go
  7. 142 0
      types.go

+ 7 - 1
README.md

@@ -1 +1,7 @@
-有向无环图
+# 有向无环图
+
+> 加载、解析dag.yaml耗时: 515.535µs
+可以根据需要对dag对象做缓存
+
+每次执行的是dag的实例
+

+ 90 - 0
dag.yaml

@@ -0,0 +1,90 @@
+## DAG 有向无环图配置
+#
+#    / 02 \         /  07\
+# 01        04 — 05 —  08 — 10
+#    \ 03 /    \ 06 —  09 /
+#
+## DEMO
+name : "测试有向无环图"
+# 所有顶点
+vertexs : 
+  -
+      key : "01"
+      label : "第一个节点"
+      otherParam : "其他参数"
+  -
+      key : "02"
+      label : "第二个节点"
+      otherParam : "其他参数"
+  -
+      key : "03"
+      label : "第三个节点"
+      otherParam : "其他参数"
+  -
+      key : "04"
+      label : "第四个节点"
+      otherParam : "其他参数"
+  -
+      key : "05"
+      label : "第五个节点"
+      otherParam : "其他参数"            
+  -
+      key : "06"
+      label : "第六个节点"
+      otherParam : "其他参数"  
+  -
+      key : "07"
+      label : "第七个节点"
+      otherParam : "其他参数"  
+  -
+      key : "08"
+      label : "第八个节点"
+      otherParam : "其他参数"  
+  -
+      key : "09"
+      label : "第九个节点"
+      otherParam : "其他参数"  
+  -
+      key : "10"
+      label : "第十个节点"
+      otherParam : "其他参数"                          
+# 所有连线,正方向
+edges :
+  -
+    from : "01"
+    to : "02"
+  -
+    from : "01"
+    to : "03"
+  -
+    from : "02"
+    to : "04"     
+  -
+    from : "03"
+    to : "04"   
+  -
+    from : "04"
+    to : "05"
+  -
+    from : "04"
+    to : "06"    
+  -
+    from : "05"
+    to : "07"
+  -
+    from : "05"
+    to : "08"     
+  -
+    from : "06"
+    to : "08"     
+  -
+    from : "07"
+    to : "10"     
+  -
+    from : "08"
+    to : "10"     
+  -
+    from : "09"
+    to : "10"     
+    
+         

+ 24 - 0
dag_test.go

@@ -0,0 +1,24 @@
+package dag
+
+import (
+	"log"
+	"math/rand"
+	"testing"
+	"time"
+)
+
+/*
+DAG 图
+*/
+func TestDag(t *testing.T) {
+	t1 := time.Now()
+	dag, err := LoadFromYamlFile("./dag.yaml")
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Println("加载、解析DAG耗时:", time.Since(t1))
+	RunDag(dag, 2, func(v *Vertex) {
+		log.Println(v.Key, v.Value)
+		time.Sleep(time.Duration(rand.Int63n(1500)+10) * time.Millisecond)
+	})
+}

+ 2 - 0
go.mod

@@ -1,3 +1,5 @@
 module jygit.jydev.jianyu360.cn/zhanghongbo/dag
 
 go 1.19
+
+require gopkg.in/yaml.v2 v2.4.0

+ 4 - 0
go.sum

@@ -0,0 +1,4 @@
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

+ 98 - 0
queue.go

@@ -0,0 +1,98 @@
+package dag
+
+/* 用slice实现队列
+ */
+
+// minQueueLen is smallest capacity that queue may have.
+// Must be power of 2 for bitwise modulus: x % n == x & (n - 1).
+const minQueueLen = 16
+
+// Queue represents a single instance of the queue data structure.
+type Queue struct {
+	buf               []interface{}
+	head, tail, count int
+}
+
+// New constructs and returns a new Queue.
+func NewQueue() *Queue {
+	return &Queue{
+		buf: make([]interface{}, minQueueLen),
+	}
+}
+
+// Length returns the number of elements currently stored in the queue.
+func (q *Queue) Length() int {
+	return q.count
+}
+
+// resizes the queue to fit exactly twice its current contents
+// this can result in shrinking if the queue is less than half-full
+func (q *Queue) resize() {
+	newBuf := make([]interface{}, q.count<<1)
+
+	if q.tail > q.head {
+		copy(newBuf, q.buf[q.head:q.tail])
+	} else {
+		n := copy(newBuf, q.buf[q.head:])
+		copy(newBuf[n:], q.buf[:q.tail])
+	}
+
+	q.head = 0
+	q.tail = q.count
+	q.buf = newBuf
+}
+
+// Add puts an element on the end of the queue.
+func (q *Queue) Add(elem interface{}) {
+	if q.count == len(q.buf) {
+		q.resize()
+	}
+
+	q.buf[q.tail] = elem
+	// bitwise modulus
+	q.tail = (q.tail + 1) & (len(q.buf) - 1)
+	q.count++
+}
+
+// Peek returns the element at the head of the queue. This call panics
+// if the queue is empty.
+func (q *Queue) Peek() interface{} {
+	if q.count <= 0 {
+		panic("queue: Peek() called on empty queue")
+	}
+	return q.buf[q.head]
+}
+
+// Get returns the element at index i in the queue. If the index is
+// invalid, the call will panic. This method accepts both positive and
+// negative index values. Index 0 refers to the first element, and
+// index -1 refers to the last.
+func (q *Queue) Get(i int) interface{} {
+	// If indexing backwards, convert to positive index.
+	if i < 0 {
+		i += q.count
+	}
+	if i < 0 || i >= q.count {
+		panic("queue: Get() called with index out of range")
+	}
+	// bitwise modulus
+	return q.buf[(q.head+i)&(len(q.buf)-1)]
+}
+
+// Remove removes and returns the element from the front of the queue. If the
+// queue is empty, the call will panic.
+func (q *Queue) Remove() interface{} {
+	if q.count <= 0 {
+		panic("queue: Remove() called on empty queue")
+	}
+	ret := q.buf[q.head]
+	q.buf[q.head] = nil
+	// bitwise modulus
+	q.head = (q.head + 1) & (len(q.buf) - 1)
+	q.count--
+	// Resize down if buffer 1/4 full.
+	if len(q.buf) > minQueueLen && (q.count<<2) == len(q.buf) {
+		q.resize()
+	}
+	return ret
+}

+ 142 - 0
types.go

@@ -0,0 +1,142 @@
+package dag
+
+import (
+	"io/ioutil"
+	"sync"
+
+	"gopkg.in/yaml.v2"
+)
+
+type (
+	// 图结构
+	DAG struct {
+		Name    string
+		Vertexs []*Vertex
+	}
+
+	// 顶点
+	Vertex struct {
+		Key      string //名称
+		Value    interface{}
+		Parents  []*Vertex
+		Children []*Vertex
+	}
+
+	// 配置文件项
+	Config struct {
+		Name    string `yaml:"name"`
+		Vectexs []*struct {
+			Key        string      `yaml:"key"`
+			Label      string      `yaml:"label"`
+			OtherParam interface{} `yaml:"otherParam"`
+		} `yaml:"vertexs"`
+		Edges []*struct {
+			From string `yaml:"from"`
+			To   string `yaml:"to"`
+		} `yaml:"edges"`
+	}
+)
+
+// 添加顶点
+func (dag *DAG) AddVertex(v *Vertex) {
+	dag.Vertexs = append(dag.Vertexs, v)
+}
+
+// 添加顶点
+func (dag *DAG) AddVertexs(v ...*Vertex) {
+	dag.Vertexs = append(dag.Vertexs, v...)
+}
+
+// 添加边,连线
+func (dag *DAG) AddEdge(from, to *Vertex) {
+	from.Children = append(from.Children, to)
+	to.Parents = append(to.Parents, from)
+}
+
+// bfsRange 广度优先,双层切片
+func bfsRange(root *Vertex) [][]*Vertex {
+	q := NewQueue()
+	q.Add(root)
+	visited := make(map[string]*Vertex)
+	all := make([][]*Vertex, 0)
+	for q.Length() > 0 {
+		qSize := q.Length()
+		tmp := make([]*Vertex, 0)
+		for i := 0; i < qSize; i++ {
+			//pop vertex
+			currVert := q.Remove().(*Vertex)
+			if _, ok := visited[currVert.Key]; ok {
+				continue
+			}
+			visited[currVert.Key] = currVert
+			tmp = append(tmp, currVert)
+			for _, val := range currVert.Children {
+				if _, ok := visited[val.Key]; !ok {
+					q.Add(val) //add child
+				}
+			}
+		}
+		all = append([][]*Vertex{tmp}, all...)
+	}
+	return all
+}
+
+// RunDag 并行执行任务
+func RunDag(dat *DAG, threads int, fn func(v *Vertex)) {
+	root := dat.Vertexs[0]
+	all := bfsRange(root)
+
+	//
+	runTaskFn := func(v *Vertex, wg *sync.WaitGroup, lock <-chan bool) {
+		defer func() {
+			wg.Done()
+			<-lock
+		}()
+		fn(v)
+	}
+	//concurrentExecutionFn
+	concurrentExecutionFn := func(layer []*Vertex) {
+		lock := make(chan bool, threads)
+		wg := new(sync.WaitGroup)
+		for _, v := range layer {
+			wg.Add(1)
+			lock <- true
+			go runTaskFn(v, wg, lock)
+		}
+		wg.Wait()
+	}
+	//
+	for _, layer := range all {
+		concurrentExecutionFn(layer)
+	}
+}
+
+// LoadFromYaml 节点,边,添加的顺序都需要颠倒过来
+func LoadFromYamlFile(f string) (*DAG, error) {
+	bs, err := ioutil.ReadFile(f)
+	if err != nil {
+		return nil, err
+	}
+	dag := &DAG{}
+	cf := new(Config)
+	err = yaml.Unmarshal(bs, cf)
+	if err != nil {
+		return nil, err
+	}
+	cache := map[string]*Vertex{}
+	arr := make([]*Vertex, len(cf.Vectexs), len(cf.Vectexs))
+	for i, v := range cf.Vectexs {
+		vertex := &Vertex{
+			Key:   v.Key,
+			Value: v.Label,
+		}
+		arr[len(cf.Vectexs)-i-1] = vertex
+		cache[v.Key] = vertex
+	}
+	dag.AddVertexs(arr...)
+	for i := len(cf.Edges); i > 0; i-- {
+		edge := cf.Edges[i-1]
+		dag.AddEdge(cache[edge.To], cache[edge.From])
+	}
+	return dag, nil
+}