projet.go 10.4 KB
Newer Older
D
dogsheng 已提交
1 2
/*
 * Copyright (c) 2019 Huawei Technologies Co., Ltd.
3 4 5
 * A-Tune is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
6
 *     http://license.coscl.org.cn/MulanPSL2
D
dogsheng 已提交
7 8 9
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
10
 * See the Mulan PSL v2 for more details.
D
dogsheng 已提交
11 12 13 14 15 16 17
 * Create: 2019-10-29
 */

package project

import (
	"fmt"
18
	PB "gitee.com/openeuler/A-Tune/api/profile"
19 20
	"gitee.com/openeuler/A-Tune/common/log"
	"gitee.com/openeuler/A-Tune/common/utils"
D
dogsheng 已提交
21 22 23
	"os/exec"
	"strconv"
	"strings"
24 25 26
	"time"
)

27 28 29 30 31 32 33 34
const (
	LESS      = "less"
	GREATER   = "greater"
	DEPEND_ON = "depend_on"
	RELY_ON   = "rely_on"
	MULTIPLE  = "multiple"
)

35 36 37
const (
	BASE_BENCHMARK_VALUE = "baseValue"
	MIN_BENCHMARK_VALUE  = "minValue"
D
dogsheng 已提交
38 39 40 41
)

// Evaluate :store the evaluate object
type Evaluate struct {
Z
Zhipeng Xie 已提交
42 43
	Name string   `yaml:"name"`
	Info EvalInfo `yaml:"info"`
D
dogsheng 已提交
44 45 46 47
}

// EvalInfo :store the evaluation object
type EvalInfo struct {
Z
Zhipeng Xie 已提交
48 49 50 51
	Get       string `yaml:"get"`
	Type      string `yaml:"type"`
	Weight    int64  `yaml:"weight"`
	Threshold int64  `yaml:"threshold"`
D
dogsheng 已提交
52 53
}

Z
Zhipeng Xie 已提交
54 55
// YamlPrjCli :store the client yaml project
type YamlPrjCli struct {
56 57 58 59 60 61 62 63
	Project             string     `yaml:"project"`
	Iterations          int32      `yaml:"iterations"`
	RandomStarts        int32      `yaml:"random_starts"`
	Benchmark           string     `yaml:"benchmark"`
	Engine              string     `yaml:"engine"`
	FeatureFilterEngine string     `yaml:"feature_filter_engine"`
	FeatureFilterCycle  int32      `yaml:"feature_filter_cycle"`
	FeatureFilterIters  int32      `yaml:"feature_filter_iters"`
64
	SplitCount          int32      `yaml:"split_count"`
65 66
	Evaluations         []Evaluate `yaml:"evaluations"`
	StartsTime          time.Time  `yaml:"-"`
67
	TotalTime           int64      `yaml:"-"`
68 69 70 71 72
	EvalMin             float64    `yaml:"-"`
	EvalBase            float64    `yaml:"-"`
	EvalCurrent         float64    `yaml:"-"`
	StartIters          int32      `yaml:"-"`
	Params              string     `yaml:"-"`
73
	FeatureFilter       bool       `yaml:"-"`
Z
Zhipeng Xie 已提交
74 75 76 77
}

// YamlPrjSvr :store the server yaml project
type YamlPrjSvr struct {
78 79 80 81 82
	Project       string        `yaml:"project"`
	Object        []*YamlPrjObj `yaml:"object"`
	Maxiterations int32         `yaml:"maxiterations"`
	Startworkload string        `yaml:"startworkload"`
	Stopworkload  string        `yaml:"stopworkload"`
D
dogsheng 已提交
83 84 85 86
}

// YamlObj :yaml Object
type YamlObj struct {
87 88 89 90 91 92 93 94 95 96 97 98 99
	Name        string    `yaml:"name"`
	Desc        string    `yaml:"desc"`
	GetScript   string    `yaml:"get"`
	SetScript   string    `yaml:"set"`
	Needrestart string    `yaml:"needrestart"`
	Skip        bool      `yaml:"skip"`
	Type        string    `yaml:"type"`
	Step        float32   `yaml:"step,omitempty"`
	Items       []float32 `yaml:"items"`
	Options     []string  `yaml:"options"`
	Scope       []float32 `yaml:"scope,flow"`
	Dtype       string    `yaml:"dtype"`
	Ref         string    `yaml:"ref"`
D
dogsheng 已提交
100 101 102 103
}

// YamlPrjObj :store the yaml object
type YamlPrjObj struct {
104 105 106 107 108 109 110 111 112 113
	Name      string          `yaml:"name"`
	Info      YamlObj         `yaml:"info"`
	Relations []*RelationShip `yaml:"relationships"`
}

type RelationShip struct {
	Type    string `yaml:"type"`
	Target  string `yaml:"target"`
	Value   string `yaml:"value"`
	SrcName string `yaml:"src_name"`
D
dogsheng 已提交
114 115 116
}

// BenchMark method call the benchmark script
117
func (y *YamlPrjCli) BenchMark() (string, error) {
118
	benchStr := make([]string, 0)
Z
Zhipeng Xie 已提交
119

120
	benchOutByte, err := ExecCommand(y.Benchmark)
D
dogsheng 已提交
121
	if err != nil {
122
		fmt.Println(string(benchOutByte))
123
		return "", fmt.Errorf("failed to run benchmark, err: %v", err)
D
dogsheng 已提交
124
	}
125

126
	var sum float64
D
dogsheng 已提交
127
	for _, evaluation := range y.Evaluations {
Z
Zhipeng Xie 已提交
128
		newScript := strings.Replace(evaluation.Info.Get, "$out", string(benchOutByte), -1)
129
		bout, err := ExecCommand(newScript)
D
dogsheng 已提交
130
		if err != nil {
H
hanxinke 已提交
131
			err = fmt.Errorf("failed to exec %s, err: %v", newScript, err)
132
			return strings.Join(benchStr, ","), err
D
dogsheng 已提交
133
		}
134 135

		floatOut, err := strconv.ParseFloat(strings.Replace(string(bout), "\n", "", -1), 64)
D
dogsheng 已提交
136
		if err != nil {
H
hanxinke 已提交
137
			err = fmt.Errorf("failed to parse float, err: %v", err)
138
			return strings.Join(benchStr, ","), err
D
dogsheng 已提交
139 140
		}

141
		out := strconv.FormatFloat(floatOut*float64(evaluation.Info.Weight)/100, 'f', -1, 64)
D
dogsheng 已提交
142 143
		if evaluation.Info.Type == "negative" {
			out = "-" + out
144 145 146
			sum += -floatOut
		} else {
			sum += floatOut
D
dogsheng 已提交
147
		}
148
		benchStr = append(benchStr, evaluation.Name+"="+out)
D
dogsheng 已提交
149
	}
Z
Zhipeng Xie 已提交
150

151 152 153 154
	if utils.IsEquals(y.EvalBase, 0.0) {
		y.EvalBase = sum
		y.EvalMin = sum
	}
155 156 157
	if !y.FeatureFilter && sum < y.EvalMin {
		y.EvalMin = sum
	}
158
	y.EvalCurrent = sum
159
	return strings.Join(benchStr, ","), nil
D
dogsheng 已提交
160 161
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175
// Threshold return the threshold, which replace with the benchmark result.
// it is used when the parameters is not match with the relations
func (y *YamlPrjCli) Threshold() (string, error) {
	benchStr := make([]string, 0)
	for _, evaluation := range y.Evaluations {
		out := strconv.FormatFloat(float64(evaluation.Info.Threshold)*float64(evaluation.Info.Weight)/100, 'f', -1, 64)
		if evaluation.Info.Type == "negative" {
			out = "-" + out
		}
		benchStr = append(benchStr, evaluation.Name+"="+out)
	}
	return strings.Join(benchStr, ","), nil
}

176
// SetHistoryEvalBase method call the set the current EvalBase to history baseline
177
func (y *YamlPrjCli) SetHistoryEvalBase(tuningHistory *PB.TuningHistory) {
178 179 180 181
	if !utils.IsEquals(y.EvalBase, 0.0) {
		return
	}

182 183 184 185 186 187 188
	evaluation := strings.Split(tuningHistory.BaseEval, "=")
	if len(evaluation) != 2 {
		return
	}

	evalBase, _ := strconv.ParseFloat(evaluation[1], 64)
	y.EvalBase = evalBase
189

190 191 192
	minEval := strings.Split(tuningHistory.MinEval, "=")
	if len(minEval) != 2 {
		return
193
	}
194 195 196 197 198

	y.EvalMin, _ = strconv.ParseFloat(minEval[1], 64)

	y.TotalTime = tuningHistory.TotalTime
	y.StartIters = tuningHistory.Starts
199 200 201 202 203
}

// ImproveRateString method return the string format of performance improve rate
func (y *YamlPrjCli) ImproveRateString(current float64) string {
	if y.EvalBase < 0 {
204
		return fmt.Sprintf("%.2f", (current-y.EvalBase)/y.EvalBase*100)
205 206 207 208 209
	}

	return fmt.Sprintf("%.2f", (y.EvalBase-current)/current*100)
}

Z
Zhipeng Xie 已提交
210
// RunSet method call the set script to set the value
H
hanxinke 已提交
211
func (y *YamlPrjSvr) RunSet(optStr string) (error, string) {
D
dogsheng 已提交
212 213 214 215
	paraMap := make(map[string]string)
	paraSlice := strings.Split(optStr, ",")
	for _, para := range paraSlice {
		kvs := strings.Split(para, "=")
Z
Zhipeng Xie 已提交
216 217 218
		if len(kvs) < 2 {
			continue
		}
D
dogsheng 已提交
219 220
		paraMap[kvs[0]] = kvs[1]
	}
221
	log.Infof("before change paraMap: %+v\n", paraMap)
H
hanxinke 已提交
222
	scripts := make([]string, 0)
D
dogsheng 已提交
223
	for _, obj := range y.Object {
224 225 226 227 228
		if obj.Info.Skip {
			log.Infof("item %s is skiped", obj.Name)
			continue
		}

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
		var objName string
		active := true
		for _, relation := range obj.Relations {
			if relation.Type == RELY_ON && paraMap[relation.Target] != relation.Value {
				active = false
				break
			}
			if relation.Type == DEPEND_ON && paraMap[relation.Target] != relation.Value {
				continue
			}

			if relation.Type == DEPEND_ON {
				objName = relation.SrcName
			}

			if relation.Type == MULTIPLE {
				targetValue, _ := strconv.ParseFloat(paraMap[relation.Target], 32)
				objValue, _ := strconv.ParseFloat(paraMap[obj.Name], 32)
				paraMap[obj.Name] = strconv.Itoa(int(targetValue * objValue))
				continue
			}
		}

		if !active {
			log.Infof("%s value is not match the relations", obj.Name)
			continue
		}

257 258
		out, err := ExecCommand(obj.Info.GetScript)
		if err != nil {
H
hanxinke 已提交
259
			return fmt.Errorf("failed to exec %s, err: %v", obj.Info.GetScript, err), ""
260 261 262 263 264 265
		}

		if strings.TrimSpace(string(out)) == paraMap[obj.Name] {
			log.Infof("%s need not be set, value is %s", obj.Name, paraMap[obj.Name])
			continue
		}
266

D
dogsheng 已提交
267
		script := obj.Info.SetScript
268 269 270 271 272 273
		var newScript string
		if len(strings.Fields(paraMap[obj.Name])) > 1 {
			newScript = strings.Replace(script, "$value", "\""+paraMap[obj.Name]+"\"", -1)
		} else {
			newScript = strings.Replace(script, "$value", paraMap[obj.Name], -1)
		}
274 275

		newScript = strings.Replace(newScript, "$name", objName, -1)
D
dogsheng 已提交
276
		log.Info("set script:", newScript)
277
		_, err = ExecCommand(newScript)
D
dogsheng 已提交
278
		if err != nil {
H
hanxinke 已提交
279
			return fmt.Errorf("failed to exec %s, err: %v", newScript, err), ""
D
dogsheng 已提交
280
		}
H
hanxinke 已提交
281
		scripts = append(scripts, newScript)
D
dogsheng 已提交
282
	}
283
	log.Infof("after change paraMap: %+v\n", paraMap)
H
hanxinke 已提交
284
	return nil, strings.Join(scripts, ",")
D
dogsheng 已提交
285 286 287
}

// RestartProject method call the StartWorkload and StopWorkload script to restart the service
H
hanxinke 已提交
288
func (y *YamlPrjSvr) RestartProject() (error, string) {
D
dogsheng 已提交
289 290 291 292
	startWorkload := y.Startworkload
	stopWorkload := y.Stopworkload

	needRestart := false
Z
Zhipeng Xie 已提交
293

D
dogsheng 已提交
294 295 296 297 298 299
	for _, obj := range y.Object {
		if obj.Info.Needrestart == "true" {
			needRestart = true
			break
		}
	}
H
hanxinke 已提交
300 301

	scripts := make([]string, 0)
Z
Zhipeng Xie 已提交
302
	if needRestart {
303
		out, err := ExecCommand(stopWorkload)
D
dogsheng 已提交
304
		if err != nil {
H
hanxinke 已提交
305
			return fmt.Errorf("failed to exec %s, err: %v", stopWorkload, err), ""
D
dogsheng 已提交
306 307
		}
		log.Debug(string(out))
H
hanxinke 已提交
308
		scripts = append(scripts, stopWorkload)
D
dogsheng 已提交
309

310
		log.Debugf("start workload script is: %s", startWorkload)
311
		out, err = ExecCommand(startWorkload)
D
dogsheng 已提交
312
		if err != nil {
H
hanxinke 已提交
313
			return fmt.Errorf("failed to exec %s, err: %v", startWorkload, err), ""
D
dogsheng 已提交
314
		}
315
		log.Debug(string(out))
H
hanxinke 已提交
316 317

		scripts = append(scripts, startWorkload)
D
dogsheng 已提交
318 319
	}

H
hanxinke 已提交
320
	return nil, strings.Join(scripts, ",")
D
dogsheng 已提交
321
}
322

323 324 325 326 327 328 329 330
// MergeProject two yaml project to one object
func (y *YamlPrjSvr) MergeProject(prj *YamlPrjSvr) error {
	for _, obj := range prj.Object {
		y.Object = append(y.Object, obj)
	}
	return nil
}

331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
// MatchRelations method check if the params match the relations
// if less or greater is not match, return false, else return true
func (y *YamlPrjSvr) MatchRelations(optStr string) bool {
	paraMap := make(map[string]string)
	paraSlice := strings.Split(optStr, ",")
	for _, para := range paraSlice {
		kvs := strings.Split(para, "=")
		if len(kvs) < 2 {
			continue
		}
		paraMap[kvs[0]] = kvs[1]
	}

	for _, obj := range y.Object {
		if obj.Info.Skip {
			log.Infof("item %s is skiped", obj.Name)
			continue
		}
		for _, relation := range obj.Relations {
			if relation.Type != LESS && relation.Type != GREATER {
				continue
			}

			targetValue, _ := strconv.Atoi(paraMap[relation.Target])
			objValue, _ := strconv.Atoi(paraMap[obj.Name])

			if relation.Type == LESS && objValue > targetValue {
				return false
			}

			if relation.Type == GREATER && objValue < targetValue {
				return false
			}
		}
	}
	return true
}

369 370 371
//exec command and get result
func ExecCommand(script string) ([]byte, error) {
	cmd := exec.Command("sh", "-c", script)
H
hanxinke 已提交
372
	return cmd.CombinedOutput()
373
}