গতকাল আমি "। নেট স্ট্রাক্ট পারফরম্যান্স" শিরোনামে ক্রিস্টোফ নাহরের একটি নিবন্ধ পেয়েছি যা একটি পদ্ধতির জন্য দুটি পয়েন্ট স্ট্রাক্ট ( double
টিপলস) যুক্ত করার জন্য কয়েকটি ভাষা (সি ++, সি #, জাভা, জাভাস্ক্রিপ্ট) বেঞ্চমার্ক করেছে ।
দেখা গেছে, সি ++ সংস্করণটি কার্যকর করতে প্রায় 1000 মিমি লাগবে (1e9 পুনরাবৃত্তি), যখন সি # একই মেশিনে 000 3000ms এর অধীনে পেতে পারে না (এবং x64 এর চেয়েও খারাপ সম্পাদন করে)।
এটি নিজে পরীক্ষা করার জন্য, আমি সি # কোডটি নিয়েছি (এবং কেবলমাত্র সেই পদ্ধতিটি কল করার জন্য সামান্য সরলীকৃত যেখানে প্যারামিটারগুলি মান দ্বারা পাস হয়), এবং এটি i7-3610QM মেশিনে (একক কোরের জন্য 3.1 গিগাহার্টজ বুস্ট), 8 জিবি র্যাম, উইন 8 নিয়ে চলেছি। 1, .NET 4.5.2 ব্যবহার করে, 32-বিট বিল্ড রিলিজ করুন (আমার ওএস 64-বিট হওয়ায় x86 ওউ 64)। এটি সরলিকৃত সংস্করণ:
public static class CSharpTest
{
private const int ITERATIONS = 1000000000;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Point AddByVal(Point a, Point b)
{
return new Point(a.X + b.Y, a.Y + b.X);
}
public static void Main()
{
Point a = new Point(1, 1), b = new Point(1, 1);
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < ITERATIONS; i++)
a = AddByVal(a, b);
sw.Stop();
Console.WriteLine("Result: x={0} y={1}, Time elapsed: {2} ms",
a.X, a.Y, sw.ElapsedMilliseconds);
}
}
Point
সহজভাবে সংজ্ঞায়িত সহ :
public struct Point
{
private readonly double _x, _y;
public Point(double x, double y) { _x = x; _y = y; }
public double X { get { return _x; } }
public double Y { get { return _y; } }
}
এটি চালানো নিবন্ধের মতো ফলাফল তৈরি করে:
Result: x=1000000001 y=1000000001, Time elapsed: 3159 ms
প্রথম অদ্ভুত পর্যবেক্ষণ
যেহেতু পদ্ধতিটি ইনলাইন করা উচিত, তাই আমি ভাবলাম যে কোডটি যদি পুরোপুরি স্ট্রাক্টগুলি সরিয়ে ফেলা হয় এবং পুরো জিনিসটি একসাথে linedোকানো হয় তবে কোডটি কীভাবে সম্পাদন করবে:
public static class CSharpTest
{
private const int ITERATIONS = 1000000000;
public static void Main()
{
// not using structs at all here
double ax = 1, ay = 1, bx = 1, by = 1;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < ITERATIONS; i++)
{
ax = ax + by;
ay = ay + bx;
}
sw.Stop();
Console.WriteLine("Result: x={0} y={1}, Time elapsed: {2} ms",
ax, ay, sw.ElapsedMilliseconds);
}
}
এবং কার্যত একই ফলাফল পেয়েছে (বেশ কয়েকটি পুনরায় প্রচেষ্টার পরে আসলে 1% ধীর), যার অর্থ জেআইটি-টের মনে হয় যে সমস্ত ফাংশন কলকে অনুকূল করে একটি ভাল কাজ করছে:
Result: x=1000000001 y=1000000001, Time elapsed: 3200 ms
এর অর্থ হ'ল বেঞ্চমার্কটি কোনও struct
কার্যকারিতা পরিমাপ করে বলে মনে হচ্ছে না এবং এটি কেবলমাত্র মৌলিক double
গাণিতিককে পরিমাপ করে বলে মনে হচ্ছে (সমস্ত কিছু সরিয়ে নেওয়ার পরে)।
অদ্ভুত জিনিস
এখন আসে অদ্ভুত অংশ। আমি যদি লুপের বাইরে কেবল অন্য স্টপওয়াচটি জুড়ে থাকি (হ্যাঁ, বেশ কয়েকটি পুনরায় চেষ্টা করার পরে আমি এটিকে এই ক্রেজি পদক্ষেপে সংকুচিত করেছি), কোডটি তিনগুণ দ্রুত চলে :
public static void Main()
{
var outerSw = Stopwatch.StartNew(); // <-- added
{
Point a = new Point(1, 1), b = new Point(1, 1);
var sw = Stopwatch.StartNew();
for (int i = 0; i < ITERATIONS; i++)
a = AddByVal(a, b);
sw.Stop();
Console.WriteLine("Result: x={0} y={1}, Time elapsed: {2} ms",
a.X, a.Y, sw.ElapsedMilliseconds);
}
outerSw.Stop(); // <-- added
}
Result: x=1000000001 y=1000000001, Time elapsed: 961 ms
যে হাস্যকর! এবং এটি Stopwatch
আমার ভুল ফলাফল দেওয়ার মতো নয় কারণ আমি স্পষ্ট দেখতে পাচ্ছি যে এটি এক সেকেন্ড পরে শেষ হয়।
এখানে কি ঘটতে পারে কেউ আমাকে বলতে পারেন?
(হালনাগাদ)
এখানে একই প্রোগ্রামে দুটি পদ্ধতি রয়েছে যা দেখায় যে কারণটি জেটিটিং নয়:
public static class CSharpTest
{
private const int ITERATIONS = 1000000000;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Point AddByVal(Point a, Point b)
{
return new Point(a.X + b.Y, a.Y + b.X);
}
public static void Main()
{
Test1();
Test2();
Console.WriteLine();
Test1();
Test2();
}
private static void Test1()
{
Point a = new Point(1, 1), b = new Point(1, 1);
var sw = Stopwatch.StartNew();
for (int i = 0; i < ITERATIONS; i++)
a = AddByVal(a, b);
sw.Stop();
Console.WriteLine("Test1: x={0} y={1}, Time elapsed: {2} ms",
a.X, a.Y, sw.ElapsedMilliseconds);
}
private static void Test2()
{
var swOuter = Stopwatch.StartNew();
Point a = new Point(1, 1), b = new Point(1, 1);
var sw = Stopwatch.StartNew();
for (int i = 0; i < ITERATIONS; i++)
a = AddByVal(a, b);
sw.Stop();
Console.WriteLine("Test2: x={0} y={1}, Time elapsed: {2} ms",
a.X, a.Y, sw.ElapsedMilliseconds);
swOuter.Stop();
}
}
আউটপুট:
Test1: x=1000000001 y=1000000001, Time elapsed: 3242 ms
Test2: x=1000000001 y=1000000001, Time elapsed: 974 ms
Test1: x=1000000001 y=1000000001, Time elapsed: 3251 ms
Test2: x=1000000001 y=1000000001, Time elapsed: 972 ms
এখানে একটি পেস্টবিন রয়েছে। আপনাকে নেট। এনটি 4.x এ 32-বিট রিলিজ হিসাবে চালানো দরকার (এটি নিশ্চিত করার জন্য কোডটিতে কয়েকটি চেক রয়েছে)।
(আপডেট 4)
@ হান্সের উত্তরে @ উসার এর মন্তব্যে অনুসরণ করে, আমি উভয় পদ্ধতির জন্য অপ্টিমাইজড বিচ্ছিন্নতা পরীক্ষা করেছি এবং সেগুলি ভিন্ন:
এটি দেখা যাচ্ছে যে ডাবল ফিল্ড প্রান্তিককরণের চেয়ে প্রথম ক্ষেত্রে মজাদার অভিনীত সংকলকের কারণে এই পার্থক্যটি হতে পারে?
এছাড়াও, আমি যদি দুটি ভেরিয়েবল যুক্ত করি (মোট অফসেট 8 বাইট) তবে আমি এখনও একই গতি বৃদ্ধি পাবো - এবং এটি আর মনে হয় না এটি হান্স পাসেন্টের ক্ষেত্রের সারিবদ্ধ উল্লেখের সাথে সম্পর্কিত:
// this is still fast?
private static void Test3()
{
var magical_speed_booster_1 = "whatever";
var magical_speed_booster_2 = "whatever";
{
Point a = new Point(1, 1), b = new Point(1, 1);
var sw = Stopwatch.StartNew();
for (int i = 0; i < ITERATIONS; i++)
a = AddByVal(a, b);
sw.Stop();
Console.WriteLine("Test2: x={0} y={1}, Time elapsed: {2} ms",
a.X, a.Y, sw.ElapsedMilliseconds);
}
GC.KeepAlive(magical_speed_booster_1);
GC.KeepAlive(magical_speed_booster_2);
}