আপডেট: প্রাক-কম্পাইল এবং অলস-সংকলিত বেঞ্চমার্কগুলি যুক্ত করা হয়েছে
আপডেট 2: সক্রিয়, আমি ভুল। সম্পূর্ণ এবং সঠিক উত্তরের জন্য এরিক লিপার্টের পোস্টটি দেখুন। আমি এখানে বেনমার্ক সংখ্যাগুলির জন্য রেখে যাচ্ছি
* আপডেট 3: এই প্রশ্নের মার্ক গ্রেভেলের উত্তরের ভিত্তিতে আইএল-নির্গত এবং অলস আইএল-নির্গত মানদণ্ড যুক্ত হয়েছে ।
আমার জ্ঞানের কাছে, dynamic
কীওয়ার্ডটি ব্যবহারের সময় এবং নিজের সময়ে রানটাইমের সময় কোনও অতিরিক্ত সংকলন ঘটায় না (যদিও আমি কল্পনা করি যে এটি নির্দিষ্ট পরিস্থিতিতে কোন ধরণের অবজেক্টগুলি আপনার গতিশীল ভেরিয়েবলকে সমর্থন করছে তার উপর নির্ভর করে) এটি এমন পরিস্থিতিতে করতে পারে)।
পারফরম্যান্স সম্পর্কে, dynamic
অন্তর্নিহিতভাবে কিছু ওভারহেড পরিচয় করিয়ে দেয় তবে আপনি যতটা ভাবেন ততটা প্রায় নয়। উদাহরণস্বরূপ, আমি সবেমাত্র একটি মানদণ্ড চালিয়েছি যা দেখতে এরকম দেখাচ্ছে:
void Main()
{
Foo foo = new Foo();
var args = new object[0];
var method = typeof(Foo).GetMethod("DoSomething");
dynamic dfoo = foo;
var precompiled =
Expression.Lambda<Action>(
Expression.Call(Expression.Constant(foo), method))
.Compile();
var lazyCompiled = new Lazy<Action>(() =>
Expression.Lambda<Action>(
Expression.Call(Expression.Constant(foo), method))
.Compile(), false);
var wrapped = Wrap(method);
var lazyWrapped = new Lazy<Func<object, object[], object>>(() => Wrap(method), false);
var actions = new[]
{
new TimedAction("Direct", () =>
{
foo.DoSomething();
}),
new TimedAction("Dynamic", () =>
{
dfoo.DoSomething();
}),
new TimedAction("Reflection", () =>
{
method.Invoke(foo, args);
}),
new TimedAction("Precompiled", () =>
{
precompiled();
}),
new TimedAction("LazyCompiled", () =>
{
lazyCompiled.Value();
}),
new TimedAction("ILEmitted", () =>
{
wrapped(foo, null);
}),
new TimedAction("LazyILEmitted", () =>
{
lazyWrapped.Value(foo, null);
}),
};
TimeActions(1000000, actions);
}
class Foo{
public void DoSomething(){}
}
static Func<object, object[], object> Wrap(MethodInfo method)
{
var dm = new DynamicMethod(method.Name, typeof(object), new Type[] {
typeof(object), typeof(object[])
}, method.DeclaringType, true);
var il = dm.GetILGenerator();
if (!method.IsStatic)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Unbox_Any, method.DeclaringType);
}
var parameters = method.GetParameters();
for (int i = 0; i < parameters.Length; i++)
{
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldelem_Ref);
il.Emit(OpCodes.Unbox_Any, parameters[i].ParameterType);
}
il.EmitCall(method.IsStatic || method.DeclaringType.IsValueType ?
OpCodes.Call : OpCodes.Callvirt, method, null);
if (method.ReturnType == null || method.ReturnType == typeof(void))
{
il.Emit(OpCodes.Ldnull);
}
else if (method.ReturnType.IsValueType)
{
il.Emit(OpCodes.Box, method.ReturnType);
}
il.Emit(OpCodes.Ret);
return (Func<object, object[], object>)dm.CreateDelegate(typeof(Func<object, object[], object>));
}
আপনি কোড থেকে দেখতে পাচ্ছেন, আমি একটি সাধারণ নো-অপশন পদ্ধতিটি সাতটি ভিন্ন উপায়ে চেষ্টা করার চেষ্টা করব:
- সরাসরি পদ্ধতি কল
- ব্যবহার
dynamic
- প্রতিবিম্ব দ্বারা
Action
রানটাইমে প্রাক-কম্পাইল হওয়া একটি ব্যবহার করে (ফলস্বরূপ সংকলনের সময় বাদ দিয়ে)।
- একটি
Action
থ্রেড-নিরাপদ অলস ভেরিয়েবল (এটি সংকলনের সময় সহ) ব্যবহার করে প্রথমবারের জন্য প্রয়োজনীয় সংকলন করা একটি ব্যবহার করে
- পরীক্ষার আগে তৈরি হয়ে যায় এমন একটি গতিময়ভাবে তৈরি পদ্ধতি ব্যবহার
- পরীক্ষার সময় অলসভাবে তাত্ক্ষণিকভাবে গতিশীলভাবে উত্পন্ন পদ্ধতি ব্যবহার করা method
প্রত্যেকে একটি সাধারণ লুপে 1 মিলিয়ন বার কল করে। সময় ফলাফল এখানে:
সরাসরি: 3.4248ms
ডায়নামিক: 45.0728ms
প্রতিবিম্ব
: 888.4011ms
প্রাকম্পম্পিত: 21.9166ms
অলস কম্পাইল: 30.2045
মিমি ইলিডিত: 8.4918ms অলস: 14.3483ms
সুতরাং dynamic
কীওয়ার্ডটি ব্যবহারের সময় সরাসরি পদ্ধতিটি কল করার চেয়ে দীর্ঘতার অর্ডার লাগে, এটি এখনও প্রায় 50 মিলিসেকেন্ডে এক মিলিয়ন বার অপারেশনটি সম্পন্ন করতে সক্ষম হয়, এটি প্রতিবিম্বের চেয়ে অনেক দ্রুত করে তোলে। আমরা যে পদ্ধতিটি কল করি সেগুলি যদি কিছু ঘনকটি একত্রিত করা বা কোনও মানের জন্য সংগ্রহ অনুসন্ধান করার মতো কিছু নিবিড় করার চেষ্টা করা হত তবে এই ক্রিয়াকলাপগুলি সম্ভবত সরাসরি কল এবং কলের মধ্যে পার্থক্য ছাড়িয়ে যায় dynamic
।
পারফরম্যান্স dynamic
অকারণে ব্যবহার না করার অনেকগুলি ভাল কারণগুলির মধ্যে একটি , তবে আপনি যখন সত্যিকারের dynamic
ডেটা নিয়ে কাজ করছেন তখন এটি এমন সুবিধাগুলি সরবরাহ করতে পারে যা অসুবিধাগুলি ছাড়িয়ে যায়।
আপডেট 4
জনবটের মন্তব্যের ভিত্তিতে, আমি প্রতিবিম্ব অঞ্চলটি চারটি পৃথক পরীক্ষায় ভেঙে ফেলেছি:
new TimedAction("Reflection, find method", () =>
{
typeof(Foo).GetMethod("DoSomething").Invoke(foo, args);
}),
new TimedAction("Reflection, predetermined method", () =>
{
method.Invoke(foo, args);
}),
new TimedAction("Reflection, create a delegate", () =>
{
((Action)method.CreateDelegate(typeof(Action), foo)).Invoke();
}),
new TimedAction("Reflection, cached delegate", () =>
{
methodDelegate.Invoke();
}),
... এবং এখানে মাপদণ্ডের ফলাফল:
সুতরাং আপনি যদি একটি নির্দিষ্ট পদ্ধতিটি নির্ধারণ করতে পারেন যা আপনাকে প্রচুর কল করতে হবে, সেই পদ্ধতিটির উল্লেখ করে একটি ক্যাশেড প্রতিনিধিকে আহ্বান করা পদ্ধতিটির কলিংয়ের মতোই দ্রুত fast তবে, আপনি যদি কোন পদ্ধতিটি কল করতে চান ঠিক তেমন পদ্ধতি নির্ধারণের প্রয়োজন হয় তবে এর জন্য একটি প্রতিনিধি তৈরি করা খুব ব্যয়বহুল।