209 lines
4.8 KiB
Go
209 lines
4.8 KiB
Go
package profiling
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"sort"
|
||
|
|
"sync"
|
||
|
|
)
|
||
|
|
|
||
|
|
// TaskProfile represents profiling data for a task
|
||
|
|
type TaskProfile struct {
|
||
|
|
TaskID string
|
||
|
|
Duration float64
|
||
|
|
CPUUsage float64
|
||
|
|
MemUsage float64
|
||
|
|
Throughput float64
|
||
|
|
}
|
||
|
|
|
||
|
|
// WorkflowProfile represents profiling for an entire workflow
|
||
|
|
type WorkflowProfile struct {
|
||
|
|
WorkflowID string
|
||
|
|
Tasks map[string]*TaskProfile
|
||
|
|
TotalDuration float64
|
||
|
|
CriticalPath []string
|
||
|
|
}
|
||
|
|
|
||
|
|
// WorkflowProfiler profiles workflow execution
|
||
|
|
type WorkflowProfiler struct {
|
||
|
|
mu sync.RWMutex
|
||
|
|
profiles map[string]*WorkflowProfile
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewWorkflowProfiler creates a new workflow profiler
|
||
|
|
func NewWorkflowProfiler() *WorkflowProfiler {
|
||
|
|
return &WorkflowProfiler{
|
||
|
|
profiles: make(map[string]*WorkflowProfile),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// RecordTaskExecution records task execution metrics
|
||
|
|
func (wp *WorkflowProfiler) RecordTaskExecution(workflowID, taskID string, duration, cpu, mem float64) {
|
||
|
|
wp.mu.Lock()
|
||
|
|
defer wp.mu.Unlock()
|
||
|
|
|
||
|
|
if _, exists := wp.profiles[workflowID]; !exists {
|
||
|
|
wp.profiles[workflowID] = &WorkflowProfile{
|
||
|
|
WorkflowID: workflowID,
|
||
|
|
Tasks: make(map[string]*TaskProfile),
|
||
|
|
CriticalPath: make([]string, 0),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
profile := wp.profiles[workflowID]
|
||
|
|
profile.Tasks[taskID] = &TaskProfile{
|
||
|
|
TaskID: taskID,
|
||
|
|
Duration: duration,
|
||
|
|
CPUUsage: cpu,
|
||
|
|
MemUsage: mem,
|
||
|
|
Throughput: 1000.0 / duration, // Tasks per second
|
||
|
|
}
|
||
|
|
|
||
|
|
// Recalculate total duration
|
||
|
|
total := 0.0
|
||
|
|
for _, tp := range profile.Tasks {
|
||
|
|
if tp.Duration > total {
|
||
|
|
total = tp.Duration
|
||
|
|
}
|
||
|
|
}
|
||
|
|
profile.TotalDuration = total
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetSlowTasks returns tasks sorted by duration (slowest first)
|
||
|
|
func (wp *WorkflowProfiler) GetSlowTasks(workflowID string, limit int) []string {
|
||
|
|
wp.mu.RLock()
|
||
|
|
defer wp.mu.RUnlock()
|
||
|
|
|
||
|
|
profile, exists := wp.profiles[workflowID]
|
||
|
|
if !exists {
|
||
|
|
return []string{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Sort tasks by duration
|
||
|
|
type taskDuration struct {
|
||
|
|
taskID string
|
||
|
|
duration float64
|
||
|
|
}
|
||
|
|
|
||
|
|
tasks := make([]taskDuration, 0)
|
||
|
|
for taskID, tp := range profile.Tasks {
|
||
|
|
tasks = append(tasks, taskDuration{taskID, tp.Duration})
|
||
|
|
}
|
||
|
|
|
||
|
|
sort.Slice(tasks, func(i, j int) bool {
|
||
|
|
return tasks[i].duration > tasks[j].duration
|
||
|
|
})
|
||
|
|
|
||
|
|
result := make([]string, 0)
|
||
|
|
for i, t := range tasks {
|
||
|
|
if i >= limit {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
result = append(result, t.taskID)
|
||
|
|
}
|
||
|
|
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetHighCPUTasks returns tasks with high CPU usage
|
||
|
|
func (wp *WorkflowProfiler) GetHighCPUTasks(workflowID string, threshold float64) []string {
|
||
|
|
wp.mu.RLock()
|
||
|
|
defer wp.mu.RUnlock()
|
||
|
|
|
||
|
|
profile, exists := wp.profiles[workflowID]
|
||
|
|
if !exists {
|
||
|
|
return []string{}
|
||
|
|
}
|
||
|
|
|
||
|
|
result := make([]string, 0)
|
||
|
|
for taskID, tp := range profile.Tasks {
|
||
|
|
if tp.CPUUsage > threshold {
|
||
|
|
result = append(result, taskID)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetHighMemTasks returns tasks with high memory usage
|
||
|
|
func (wp *WorkflowProfiler) GetHighMemTasks(workflowID string, threshold float64) []string {
|
||
|
|
wp.mu.RLock()
|
||
|
|
defer wp.mu.RUnlock()
|
||
|
|
|
||
|
|
profile, exists := wp.profiles[workflowID]
|
||
|
|
if !exists {
|
||
|
|
return []string{}
|
||
|
|
}
|
||
|
|
|
||
|
|
result := make([]string, 0)
|
||
|
|
for taskID, tp := range profile.Tasks {
|
||
|
|
if tp.MemUsage > threshold {
|
||
|
|
result = append(result, taskID)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetOptimizationSuggestions returns optimization recommendations
|
||
|
|
func (wp *WorkflowProfiler) GetOptimizationSuggestions(workflowID string) []string {
|
||
|
|
wp.mu.RLock()
|
||
|
|
defer wp.mu.RUnlock()
|
||
|
|
|
||
|
|
profile, exists := wp.profiles[workflowID]
|
||
|
|
if !exists {
|
||
|
|
return []string{}
|
||
|
|
}
|
||
|
|
|
||
|
|
suggestions := make([]string, 0)
|
||
|
|
|
||
|
|
// Check for slow tasks
|
||
|
|
for taskID, tp := range profile.Tasks {
|
||
|
|
if tp.Duration > profile.TotalDuration*0.5 {
|
||
|
|
suggestions = append(suggestions, fmt.Sprintf("Task %s takes 50%% of total time, consider optimizing", taskID))
|
||
|
|
}
|
||
|
|
if tp.CPUUsage > 0.8 {
|
||
|
|
suggestions = append(suggestions, fmt.Sprintf("Task %s has high CPU usage (%.2f), consider parallelizing", taskID, tp.CPUUsage))
|
||
|
|
}
|
||
|
|
if tp.MemUsage > 0.8 {
|
||
|
|
suggestions = append(suggestions, fmt.Sprintf("Task %s has high memory usage (%.2f), consider reducing payload", taskID, tp.MemUsage))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return suggestions
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetProfile retrieves profiling data for a workflow
|
||
|
|
func (wp *WorkflowProfiler) GetProfile(workflowID string) (*WorkflowProfile, bool) {
|
||
|
|
wp.mu.RLock()
|
||
|
|
defer wp.mu.RUnlock()
|
||
|
|
|
||
|
|
profile, exists := wp.profiles[workflowID]
|
||
|
|
return profile, exists
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetTaskProfile retrieves profiling data for a specific task
|
||
|
|
func (wp *WorkflowProfiler) GetTaskProfile(workflowID, taskID string) (*TaskProfile, error) {
|
||
|
|
wp.mu.RLock()
|
||
|
|
defer wp.mu.RUnlock()
|
||
|
|
|
||
|
|
profile, exists := wp.profiles[workflowID]
|
||
|
|
if !exists {
|
||
|
|
return nil, fmt.Errorf("workflow not found: %s", workflowID)
|
||
|
|
}
|
||
|
|
|
||
|
|
taskProfile, exists := profile.Tasks[taskID]
|
||
|
|
if !exists {
|
||
|
|
return nil, fmt.Errorf("task not found: %s", taskID)
|
||
|
|
}
|
||
|
|
|
||
|
|
return taskProfile, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clear clears all profiles
|
||
|
|
func (wp *WorkflowProfiler) Clear() {
|
||
|
|
wp.mu.Lock()
|
||
|
|
defer wp.mu.Unlock()
|
||
|
|
|
||
|
|
wp.profiles = make(map[string]*WorkflowProfile)
|
||
|
|
}
|