String.StartsWith与 EndsWith在大量使用时效率果然极低
今天在写一个工具时,写着写着发现执行速度突然变慢。最后发现是string.StartsWith与EndsWith的锅,改为手动比较后,执行时间变成原来的1/5-1/10。代码如下 ,黄色背景是主要部分
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.IO; using System.Threading; using System.Text.RegularExpressions; using System; public class XLogicCodeCheck { [MenuItem("代码检查/逻辑层代码检查")] public static void StartLogicCodeCheck() { Debug.Log("逻辑层代码检查"); var path = "Assets/Scripts/Logic"; var files = Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories); var total = 0.0f; var totalFind = 0; var errLines = new List<string>(100); foreach (var item in files) { var allines = File.ReadAllLines(item); if(allines.Length > 0 && allines[0].Trim().StartsWith("//Float_Uncheck")) {//Float_Uncheck continue; } for (int i = 0; i < allines.Length; ++i) { var ltext = allines[i].Trim(); if (CheckLine(allines, i)) { var info = $"发现有问题代码,文件:{item}, 行:{i + 1}, {ltext}"; EditorUtility.DisplayDialog("逻辑层代码检查", info, "确定"); errLines.Add(info); totalFind++; } } EditorUtility.DisplayProgressBar("逻辑层代码检查", $"进度:", total++ / files.Length); } if (totalFind == 0) { EditorUtility.DisplayDialog("逻辑层代码检查", "检查完成,没有发现问题", "确定"); } else { var savePath = "Assets/FloatCheck.txt"; EditorUtility.DisplayDialog("逻辑层代码检查", $"检查完成,共发现{totalFind}处问题,已保存到文件:{savePath}", "确定"); File.WriteAllLines(savePath, errLines); } Debug.Log("逻辑层代码检查完成"); } static string[] rules = new string[] { @"[\s(]float[\s)]", @"[\s(]double[\s)]", @"\d+\.\d+", //float, double, 浮点数值 @"\sMath\.[a-zA-Z]+\s*\(", @"\sMathf\.[a-zA-Z]+\s*\(", //Math或Mathf相关API @"new\s+Random\s*\(",@"new\s+System\s*\.\s*Random\s*\(", @"Random\s*\.\s*RandomRange\s*\(", @"Random\s*\.\s*Range\s*\(", @"Random\s*\.\s*ColorHSV\s*\(",//随机数 }; static string[] excludeRules = new string[] { "Math.Max", "Math.Min", "Math.Abs" }; //排除项,不检查这些 private static bool CheckLine(string[] allines, int iline) { var codeStr = FilterComment(allines, iline).Trim(); if (codeStr.Length <= 0) return false; foreach (var item in rules) { var matches = Regex.Matches(codeStr, item); foreach (Match match in matches) { var isok = true; foreach (var exc in excludeRules) { if (match.Value.Contains(exc)) isok = false; } if (isok) return true; } } return false; } //过滤掉注释 private static string FilterComment(string[] allines, int iline) { if (InBlockComment(allines, iline)) return ""; var codeStr = allines[iline]; if (codeStr.Contains("Float_Uncheck") || codeStr.StartsWith("//") || codeStr.StartsWith("/*") || codeStr.StartsWith("#region")) { return ""; } //去除行中注释 var cmtIdx = codeStr.IndexOf("//"); if (cmtIdx < 0) { cmtIdx = codeStr.IndexOf("/*"); } if (cmtIdx >= 0) { codeStr = codeStr.Substring(0, cmtIdx); } return codeStr; } //是否在块注释中 private static bool InBlockComment(string[] allines, int iline) { for (int i = iline-1; i>=0; --i) { var line = allines[i]; var lineLen = line.Length; if(lineLen >= 2) { if (line[lineLen - 2] == '*' && line[lineLen - 1] == '/') {//line.EndsWith("*/"),这个函数非常低效 return false; } else if (line[0] == '/' && line[1] == '*') {//line.StartsWith("*/"),这个函数非常低效 return true; } } } return false; } }