My PC have a dual core CPU and I was wondering - would it be possible to reduce .NET reflection times by two, if I start processing in a two thread.
我的PC有一个双核CPU,我想知道 - 如果我开始在两个线程中处理,是否可以将.NET反射时间减少两个。
By "processing" I have meant the following:
通过“处理”我的意思是:
1.loading the assembly 2.getting all types out of it (.GetTypes()) 3.processing these types 4.querying these types for methods etc.
1.加载程序集2.从中获取所有类型(.GetTypes())3。处理这些类型4.查找方法等的这些类型
If yes - what would be the best (performance-wise) strategy:
如果是的话 - 什么是最好的(表现明智的)策略:
- load all of the assemblies in a one thread then process metainfo in a two concurent threads
- load and process each assembly in its own thread
在一个线程中加载所有程序集,然后在两个concurent线程中处理metainfo
在每个线程中加载和处理每个程序集
3 个解决方案
#1
There are a couple of things to keep in mind:
要记住以下几点:
-
Starting a new thread is expensive, so if the task is short lived the overhead may be prohibitive. By using the Thread pool instead of starting your own threads, the thread pool will make sure to reuse threads to the best of its abilities. However, launching a lot of tasks in a short time doesn't yield the best result with the current thread pool implementation.
启动一个新线程是很昂贵的,所以如果任务是短暂的,那么开销可能会过高。通过使用线程池而不是启动自己的线程,线程池将确保尽可能地重用线程。但是,在当前线程池实现中,在短时间内启动大量任务不会产生最佳结果。
-
Since your specific task involves some I/O that is probably going to be the most expensive part of the operation. Forking off several threads to wait for I/O may not yield better performance. You can't really tell until you measure it.
由于您的特定任务涉及一些I / O,这可能是操作中最昂贵的部分。分离几个线程以等待I / O可能无法产生更好的性能。在测量它之前你无法确定。
-
If you're storing the data in a shared repository or outputting to a file/the screen, you'll have to synchronize around that. This obviously reduces the concurrency.
如果您将数据存储在共享存储库中或输出到文件/屏幕,则必须同步。这显然会降低并发性。
#2
Unless you are dealing with absolutely massive libraries, reflection loading is not a terribly slow thing, in my experiences. It's possible that it would be a waste of your time.
除非你正在处理绝对庞大的库,否则根据我的经验,反射加载并不是一件非常慢的事情。它可能会浪费你的时间。
Nonetheless, check out ThreadPool.QueueUserWorkItem, makes this stuff easy.
尽管如此,请查看ThreadPool.QueueUserWorkItem,使这些内容变得简单。
foreach (Assembly a in "folder/something") {
ThreadPool.QueueUserWorkItem((object assem) => {
// do work
}, a);
}
#3
Loading and processing each assembly in a seperate thread is faster. But only a bit faster. For example you will get much better milage by caching MethodInfos.
在单独的线程中加载和处理每个组件更快。但只是快一点。例如,通过缓存MethodInfos,您将获得更好的成功。
I would question the need for such an optimisation though.
我会质疑这种优化的必要性。
results:
warm up single threaded Time Elapsed 465 ms multi threaded Time Elapsed 173 ms single threaded Time Elapsed 456 ms
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Diagnostics;
namespace ConsoleApplication12 {
class Program {
static void TimeAction(string description, Action func) {
var watch = new Stopwatch();
watch.Start();
func();
watch.Stop();
Console.Write(description);
Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}
static void Main(string[] args) {
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// warm up
TimeAction("warm up single threaded", () =>
{
foreach (var assembly in assemblies) {
assembly.GetTypes().Select(type => type.GetMethods()).ToArray();
}
});
List<Thread> threads = new List<Thread>();
TimeAction("multi threaded", () => {
foreach (var assembly in assemblies) {
Thread t = new Thread(new ThreadStart( () =>
assembly.GetTypes().Select(type => type.GetMethods()).ToArray()
));
t.Start();
threads.Add(t);
}
foreach (var thread in threads) {
thread.Join();
}
});
TimeAction("single threaded", () =>
{
foreach (var assembly in assemblies) {
assembly.GetTypes().Select(type => type.GetMethods()).ToArray();
}
});
Console.ReadKey();
}
}
}
#1
There are a couple of things to keep in mind:
要记住以下几点:
-
Starting a new thread is expensive, so if the task is short lived the overhead may be prohibitive. By using the Thread pool instead of starting your own threads, the thread pool will make sure to reuse threads to the best of its abilities. However, launching a lot of tasks in a short time doesn't yield the best result with the current thread pool implementation.
启动一个新线程是很昂贵的,所以如果任务是短暂的,那么开销可能会过高。通过使用线程池而不是启动自己的线程,线程池将确保尽可能地重用线程。但是,在当前线程池实现中,在短时间内启动大量任务不会产生最佳结果。
-
Since your specific task involves some I/O that is probably going to be the most expensive part of the operation. Forking off several threads to wait for I/O may not yield better performance. You can't really tell until you measure it.
由于您的特定任务涉及一些I / O,这可能是操作中最昂贵的部分。分离几个线程以等待I / O可能无法产生更好的性能。在测量它之前你无法确定。
-
If you're storing the data in a shared repository or outputting to a file/the screen, you'll have to synchronize around that. This obviously reduces the concurrency.
如果您将数据存储在共享存储库中或输出到文件/屏幕,则必须同步。这显然会降低并发性。
#2
Unless you are dealing with absolutely massive libraries, reflection loading is not a terribly slow thing, in my experiences. It's possible that it would be a waste of your time.
除非你正在处理绝对庞大的库,否则根据我的经验,反射加载并不是一件非常慢的事情。它可能会浪费你的时间。
Nonetheless, check out ThreadPool.QueueUserWorkItem, makes this stuff easy.
尽管如此,请查看ThreadPool.QueueUserWorkItem,使这些内容变得简单。
foreach (Assembly a in "folder/something") {
ThreadPool.QueueUserWorkItem((object assem) => {
// do work
}, a);
}
#3
Loading and processing each assembly in a seperate thread is faster. But only a bit faster. For example you will get much better milage by caching MethodInfos.
在单独的线程中加载和处理每个组件更快。但只是快一点。例如,通过缓存MethodInfos,您将获得更好的成功。
I would question the need for such an optimisation though.
我会质疑这种优化的必要性。
results:
warm up single threaded Time Elapsed 465 ms multi threaded Time Elapsed 173 ms single threaded Time Elapsed 456 ms
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Diagnostics;
namespace ConsoleApplication12 {
class Program {
static void TimeAction(string description, Action func) {
var watch = new Stopwatch();
watch.Start();
func();
watch.Stop();
Console.Write(description);
Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}
static void Main(string[] args) {
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// warm up
TimeAction("warm up single threaded", () =>
{
foreach (var assembly in assemblies) {
assembly.GetTypes().Select(type => type.GetMethods()).ToArray();
}
});
List<Thread> threads = new List<Thread>();
TimeAction("multi threaded", () => {
foreach (var assembly in assemblies) {
Thread t = new Thread(new ThreadStart( () =>
assembly.GetTypes().Select(type => type.GetMethods()).ToArray()
));
t.Start();
threads.Add(t);
}
foreach (var thread in threads) {
thread.Join();
}
});
TimeAction("single threaded", () =>
{
foreach (var assembly in assemblies) {
assembly.GetTypes().Select(type => type.GetMethods()).ToArray();
}
});
Console.ReadKey();
}
}
}