add performance reporting
costs ~9% bomb performance; can be toggled in Rán REPL via 'metrics'
This commit is contained in:
		
							parent
							
								
									2ec417da6a
								
							
						
					
					
						commit
						8a22c7bf29
					
				| 
						 | 
				
			
			@ -1,13 +1,76 @@
 | 
			
		|||
package pixelflut
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// @speed: add some performance reporting mechanism on these functions when
 | 
			
		||||
//   called as goroutines
 | 
			
		||||
// Performance contains pixelflut metrics
 | 
			
		||||
type Performance struct {
 | 
			
		||||
	Enabled     bool
 | 
			
		||||
	Conns       int
 | 
			
		||||
	BytesPerSec int
 | 
			
		||||
	BytesTotal  int
 | 
			
		||||
 | 
			
		||||
	connsReporter chan int
 | 
			
		||||
	bytesReporter chan int
 | 
			
		||||
	bytes         int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p Performance) String() string {
 | 
			
		||||
	return fmt.Sprintf("%v conns\t%v\t%v/s",
 | 
			
		||||
		p.Conns, fmtBytes(p.BytesTotal), fmtBytes(p.BytesPerSec))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// https://yourbasic.org/golang/byte-count.go
 | 
			
		||||
func fmtBytes(b int) string {
 | 
			
		||||
	const unit = 1024
 | 
			
		||||
	if b < unit {
 | 
			
		||||
		return fmt.Sprintf("%d B", b)
 | 
			
		||||
	}
 | 
			
		||||
	div, exp := int64(unit), 0
 | 
			
		||||
	for n := b / unit; n >= unit; n /= unit {
 | 
			
		||||
		div *= unit
 | 
			
		||||
		exp++
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%.1f %ciB",
 | 
			
		||||
		float64(b)/float64(div), "KMGTPE"[exp])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PerformanceReporter provides pixelflut performance metrics, when Enabled is true.
 | 
			
		||||
//   @speed: Note that enabling  costs ~9% bomb performance under high throughput.
 | 
			
		||||
var PerformanceReporter = initPerfReporter()
 | 
			
		||||
 | 
			
		||||
// should be called only once
 | 
			
		||||
func initPerfReporter() *Performance {
 | 
			
		||||
	r := new(Performance)
 | 
			
		||||
	r.bytesReporter = make(chan int, 512)
 | 
			
		||||
	r.connsReporter = make(chan int, 512)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case b := <-r.bytesReporter:
 | 
			
		||||
				r.bytes += b
 | 
			
		||||
				r.BytesTotal += b
 | 
			
		||||
			case c := <-r.connsReporter:
 | 
			
		||||
				r.Conns += c
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			time.Sleep(time.Second)
 | 
			
		||||
			r.BytesPerSec = r.bytes
 | 
			
		||||
			r.bytes = 0
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// bombAddress writes the given message via plain TCP to the given address,
 | 
			
		||||
// as fast as possible, until stop is closed.
 | 
			
		||||
| 
						 | 
				
			
			@ -22,15 +85,21 @@ func bombAddress(message []byte, address string, stop chan bool, wg *sync.WaitGr
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func bombConn(message []byte, conn net.Conn, stop chan bool) {
 | 
			
		||||
	PerformanceReporter.connsReporter <- 1
 | 
			
		||||
	defer func() { PerformanceReporter.connsReporter <- -1 }()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-stop:
 | 
			
		||||
			return
 | 
			
		||||
		default:
 | 
			
		||||
			_, err := conn.Write(message)
 | 
			
		||||
			b, err := conn.Write(message)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
			if PerformanceReporter.Enabled {
 | 
			
		||||
				PerformanceReporter.bytesReporter <- b
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,6 +39,12 @@ type FlutTask struct {
 | 
			
		|||
 | 
			
		||||
type FlutAck struct{ Ok bool }
 | 
			
		||||
 | 
			
		||||
type FlutStatus struct {
 | 
			
		||||
	*pixelflut.Performance
 | 
			
		||||
	Ok      bool
 | 
			
		||||
	Fluting bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Hevring) Flut(task FlutTask, reply *FlutAck) error {
 | 
			
		||||
	if (h.task != FlutTask{}) {
 | 
			
		||||
		// @incomplete: stop old task if new task is received
 | 
			
		||||
| 
						 | 
				
			
			@ -56,9 +62,11 @@ func (h *Hevring) Flut(task FlutTask, reply *FlutAck) error {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Hevring) Status(x int, reply *FlutAck) error {
 | 
			
		||||
	// @incomplete: provide performance metrics
 | 
			
		||||
func (h *Hevring) Status(metrics bool, reply *FlutStatus) error {
 | 
			
		||||
	pixelflut.PerformanceReporter.Enabled = metrics
 | 
			
		||||
	reply.Performance = pixelflut.PerformanceReporter
 | 
			
		||||
	reply.Ok = true
 | 
			
		||||
	reply.Fluting = h.taskQuit != nil
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										42
									
								
								rpc/ran.go
								
								
								
								
							
							
						
						
									
										42
									
								
								rpc/ran.go
								
								
								
								
							| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
package rpc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	// "io"
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"image"
 | 
			
		||||
| 
						 | 
				
			
			@ -12,11 +11,14 @@ import (
 | 
			
		|||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/SpeckiJ/Hochwasser/pixelflut"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Rán struct {
 | 
			
		||||
	clients []*rpc.Client
 | 
			
		||||
	task    FlutTask
 | 
			
		||||
	metrics pixelflut.Performance
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SummonRán sets up the RPC master, accepting connections at addres (":1234")
 | 
			
		||||
| 
						 | 
				
			
			@ -59,11 +61,19 @@ func SummonRán(address string, stopChan chan bool, wg *sync.WaitGroup) *Rán {
 | 
			
		|||
			time.Sleep(100 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
			var clients []*rpc.Client
 | 
			
		||||
 | 
			
		||||
			r.metrics.Conns = 0
 | 
			
		||||
			r.metrics.BytesPerSec = 0
 | 
			
		||||
			r.metrics.BytesTotal = 0
 | 
			
		||||
 | 
			
		||||
			for _, c := range r.clients {
 | 
			
		||||
				status := FlutAck{}
 | 
			
		||||
				err := c.Call("Hevring.Status", 0, &status)
 | 
			
		||||
				status := FlutStatus{}
 | 
			
		||||
				err := c.Call("Hevring.Status", r.metrics.Enabled, &status)
 | 
			
		||||
				if err == nil && status.Ok {
 | 
			
		||||
					clients = append(clients, c)
 | 
			
		||||
					r.metrics.Conns += status.Conns
 | 
			
		||||
					r.metrics.BytesPerSec += status.BytesPerSec
 | 
			
		||||
					r.metrics.BytesTotal += status.BytesTotal
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if len(r.clients) != len(clients) {
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +83,16 @@ func SummonRán(address string, stopChan chan bool, wg *sync.WaitGroup) *Rán {
 | 
			
		|||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// print performance
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			time.Sleep(5 * time.Second)
 | 
			
		||||
			if r.metrics.Enabled {
 | 
			
		||||
				fmt.Println(r.metrics)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// REPL to change tasks without loosing clients
 | 
			
		||||
	go func() {
 | 
			
		||||
		scanner := bufio.NewScanner(os.Stdin)
 | 
			
		||||
| 
						 | 
				
			
			@ -86,6 +106,18 @@ func SummonRán(address string, stopChan chan bool, wg *sync.WaitGroup) *Rán {
 | 
			
		|||
					c.Call("Hevring.Stop", 0, &ack) // @speed: async
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			} else if cmd == "start" {
 | 
			
		||||
				if (r.task != FlutTask{}) {
 | 
			
		||||
					for _, c := range r.clients {
 | 
			
		||||
						ack := FlutAck{}
 | 
			
		||||
						// @speed: should send tasks async
 | 
			
		||||
						err := c.Call("Hevring.Flut", r.task, &ack)
 | 
			
		||||
						if err != nil || !ack.Ok {
 | 
			
		||||
							log.Printf("[rán] client didn't accept task")
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			} else if cmd == "img" && len(args) > 0 {
 | 
			
		||||
				// // @incomplete
 | 
			
		||||
				// path := args[0]
 | 
			
		||||
| 
						 | 
				
			
			@ -97,6 +129,10 @@ func SummonRán(address string, stopChan chan bool, wg *sync.WaitGroup) *Rán {
 | 
			
		|||
				// 	offset = image.Pt(x, y)
 | 
			
		||||
				// }
 | 
			
		||||
				// task := FlutTask{}
 | 
			
		||||
 | 
			
		||||
			} else if cmd == "metrics" {
 | 
			
		||||
				r.metrics.Enabled = !r.metrics.Enabled
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue