有很多关于在 .Net 中使用 Reflection 的速度的警告。 为了按列 Datapropertyname 对 datagridview 进行排序,我制作了一个简短的测试程序,该程序显示 Linq 按反射排序比按成员排序更快。 我不确定我是否都做得正确,请社区进行审查。
class Program {
private static Random random = new Random();
class TestClass {
public string Name { get; set; }
public int Number { get; set; }
}
static string RandomString(int length) {
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
static void SortyByReflection(List<TestClass> testlst) {
PropertyInfo prop = typeof(TestClass).GetProperties().Where(p => p.Name == "Name").FirstOrDefault();
List<TestClass> sorted = testlst.OrderBy(o => prop.GetValue(o)).ToList();
}
static void SortByMember(List<TestClass> testlst) {
List<TestClass> sorted = testlst.OrderBy(o => o.Name).ToList();
}
delegate void dRunner(List<TestClass> testlst);
static long UsedTime(dRunner testDelegate, List<TestClass> testlst) {
Stopwatch timer = new Stopwatch();
timer.Start();
testDelegate(testlst);
timer.Stop();
return timer.ElapsedTicks;
}
static void Main(string[] args) {
// make a dry run to init the program
SortByMember(new List<TestClass>());
SortyByReflection(new List<TestClass>());
List<int> lstSize = new List<int> { 100, 1000, 10000, 100000 };
foreach (int count in lstSize) {
// Init List
List<TestClass> testlst = new List<TestClass>();
for (int i = 0; i < count; i++) {
testlst.Add(new TestClass { Name = RandomString(10), Number = i });
}
List<long> reflection = new List<long>();
List<long> memberTime = new List<long>();
for (int i = 0; i < 100; i++) {
reflection.Add(UsedTime(SortyByReflection,testlst));
memberTime.Add(UsedTime(SortByMember,testlst));
}
Console.WriteLine($"{reflection.Min()} / {reflection.Max()} / {reflection.Average()} Min/ Max / Average Ticks needed for Reflection {count} size");
Console.WriteLine($"{memberTime.Min()} / {memberTime.Max()} / {memberTime.Average()} Min/ Max / Average Ticks needed for Member {count} size");
Console.WriteLine(new string('-', 80));
}
Console.WriteLine("done");
Console.ReadLine();
}
/*
* Sample output
425 / 1837 / 539,75 Min/ Max / Average Ticks needed for Reflection 100 size
479 / 1265 / 605,14 Min/ Max / Average Ticks needed for Member 100 size
--------------------------------------------------------------------------------
6251 / 11819 / 7309,82 Min/ Max / Average Ticks needed for Reflection 1000 size
7164 / 13369 / 8201,42 Min/ Max / Average Ticks needed for Member 1000 size
--------------------------------------------------------------------------------
76214 / 103169 / 82003,53 Min/ Max / Average Ticks needed for Reflection 10000 size
86139 / 121152 / 93201,55 Min/ Max / Average Ticks needed for Member 10000 size
--------------------------------------------------------------------------------
1092454 / 1188244 / 1139228,26 Min/ Max / Average Ticks needed for Reflection 100000 size
1225469 / 1353753 / 1280549,37 Min/ Max / Average Ticks needed for Member 100000 size
--------------------------------------------------------------------------------
done
*/
}
使用反射时,平均速度总是快 10% 左右。
实现新的(发布、在 Visual Studio 之外运行、使用结果并将 vom Order by Name 更改为 Order by Number 后,世界再次平衡。
预计新数字:
/*
97 / 1266 / 129,67 Min/ Max / Average Ticks needed for Reflection 100 size
19 / 265 / 28,34 Min/ Max / Average Ticks needed for Member 100 size
---------------------------------------------------------------------------
1064 / 2369 / 1357,42 Min/ Max / Average Ticks needed for Reflection 1000 size
218 / 598 / 290,43 Min/ Max / Average Ticks needed for Member 1000 size
---------------------------------------------------------------------------
12407 / 27326 / 15779,35 Min/ Max / Average Ticks needed for Reflection 10000 size
2703 / 5100 / 3366,52 Min/ Max / Average Ticks needed for Member 10000 size
---------------------------------------------------------------------------
147184 / 198677 / 160490,02 Min/ Max / Average Ticks needed for Reflection 100000 size
35333 / 45620 / 38493,46 Min/ Max / Average Ticks needed for Member 100000 size
-------------------------------------------------------------------------
*/
对于文件,这是新的源代码
class Program {
private static Random random = new Random();
class TestClass {
public string Name { get; set; }
public int Number { get; set; }
}
static string RandomString(int length) {
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
static List<TestClass> SortyByReflection(List<TestClass> testlst) {
PropertyInfo prop = typeof(TestClass).GetProperties().Where(p => p.Name == "Number").FirstOrDefault();
List<TestClass> sorted = testlst.OrderBy(o => prop.GetValue(o)).ToList();
return sorted;
}
static List<TestClass> SortByMember(List<TestClass> testlst) {
List<TestClass> sorted = testlst.OrderBy(o => o.Number).ToList();
return sorted;
}
delegate List<TestClass> dRunner(List<TestClass> testlst);
static List<TestClass> UsedTime(dRunner testDelegate, List<TestClass> testlst,out long time) {
time = 0;
Stopwatch timer = new Stopwatch();
timer.Start();
var x = testDelegate(testlst);
timer.Stop();
time = timer.ElapsedTicks;
return x;
}
static void Main(string[] args) {
// make a dry run to init the program
SortByMember(new List<TestClass>());
SortyByReflection(new List<TestClass>());
List<int> lstSize = new List<int> { 100, 1000, 10000, 100000 };
foreach (int count in lstSize) {
// Init List
List<TestClass> testlst = new List<TestClass>();
for (int i = 0; i < count; i++) {
testlst.Add(new TestClass { Name = RandomString(10), Number = i });
}
List<long> reflection = new List<long>();
List<long> memberTime = new List<long>();
for (int i = 0; i < 100; i++) {
foreach (var lst in UsedTime(SortyByReflection,testlst,out long time)){
var tmp = lst.Name;
tmp += lst.Number.ToString();
reflection.Add(time);
}
foreach (var lst in UsedTime(SortByMember, testlst, out long time)) {
var tmp = lst.Name;
tmp += lst.Number.ToString();
memberTime.Add(time);
}
}
Console.WriteLine($"{reflection.Min()} / {reflection.Max()} / {reflection.Average()} Min/ Max / Average Ticks needed for Reflection {count} size");
Console.WriteLine($"{memberTime.Min()} / {memberTime.Max()} / {memberTime.Average()} Min/ Max / Average Ticks needed for Member {count} size");
Console.WriteLine(new string('-', 80));
}
Console.WriteLine("done");
Console.ReadLine();
}
/*
97 / 1266 / 129,67 Min/ Max / Average Ticks needed for Reflection 100 size
19 / 265 / 28,34 Min/ Max / Average Ticks needed for Member 100 size
--------------------------------------------------------------------------------
1064 / 2369 / 1357,42 Min/ Max / Average Ticks needed for Reflection 1000 size
218 / 598 / 290,43 Min/ Max / Average Ticks needed for Member 1000 size
--------------------------------------------------------------------------------
12407 / 27326 / 15779,35 Min/ Max / Average Ticks needed for Reflection 10000 size
2703 / 5100 / 3366,52 Min/ Max / Average Ticks needed for Member 10000 size
--------------------------------------------------------------------------------
147184 / 198677 / 160490,02 Min/ Max / Average Ticks needed for Reflection 100000 size
35333 / 45620 / 38493,46 Min/ Max / Average Ticks needed for Member 100000 size
--------------------------------------------------------------------------------
*/
}
最佳答案
第一次看到这篇文章时,我并不像其他评论者那样相信它。
但是在进行了我自己的测试消除了潜在的 GC 和 JIT 影响之后,我可以确认声明是正确的 - SortyByReflection
平均而言,该方法确实比 SortByMember
更快。 .
但是,这与反射无关!
毕竟,Enumerable.OrderBy
方法执行 O(N) 选择器调用(即您尝试测量的反射与直接属性访问)和平均 O(N * Log2(N)) 比较,因此时间复杂度主要由比较操作的实现决定。
差异来了。反射方法使用Comparer<object>.Default
而另一种方法是使用 Comparer<string>.Default
。令人惊讶的是,出于某种未知的原因,前者比后者更快 string
s。我还没有测试其他数据类型(最有可能的是 int
和其他值类型,由于第一种情况涉及装箱,图片将是相反的),但 string
就是这种情况。 。我已经检查了比较器实现的引用源,并了解了可能导致它的一些想法,但这超出了当前问题的范围。
重要的是OrderBy
不适合测试反射与直接访问性能。如果将第一个方法选择器更改为 o => (string)prop.GetValue(o)
并保持第二个选择器不变,或将第二个选择器更改为 o => (object)o.Name
并保持第一个不变,运行测试将向您显示直接访问比预期的反射要快,但由于比较时间的支配,速度并没有那么快,特别是对于更大的 N
.
关于c# - 按反射排序比按成员排序更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50782194/