দুর্দান্ত প্রশ্ন।
ফিবোনাচি ফাংশনের এই মাল্টিথ্রেডেড বাস্তবায়নটি একক থ্রেডেড সংস্করণের চেয়ে দ্রুত নয় । নতুন থ্রেডিং ক্ষমতা কীভাবে কাজ করে তার খেলনা উদাহরণ হিসাবে ব্লগ পোস্টে কেবল সেই ফাংশনটি দেখানো হয়েছিল, তা হাইলাইট করে যে এটি বিভিন্ন ফাংশনে অনেকগুলি থ্রেড তৈরি করতে দেয় এবং শিডিয়ুলার একটি সর্বোত্তম কাজের চাপ বোঝে।
সমস্যাটি হ'ল @spawn
চারপাশে একটি অ-তুচ্ছ ওভারহেড রয়েছে 1µs
, সুতরাং আপনি যদি কোনও থ্রেড তুলনা করেন যে কোনও টাস্কের চেয়ে কম সময় নেয় তবে 1µs
আপনি সম্ভবত আপনার কার্য সম্পাদনকে আঘাত করেছেন। এর পুনরাবৃত্ত সংজ্ঞাটি fib(n)
অর্ডারটির ঘনঘন সময় জটিলতা 1.6180^n
[1] রয়েছে, সুতরাং যখন আপনি কল করবেন তখন আপনি fib(43)
অর্ডার 1.6180^43
থ্রেডের কিছু স্প্যান করলেন । যদি প্রত্যেকে 1µs
স্প্যান করতে নেয় তবে প্রয়োজনীয় থ্রেডগুলি স্পোন করতে এবং সময় নির্ধারণ করতে প্রায় 16 মিনিট সময় লাগবে, এবং এটি প্রকৃত গণনা করতে এবং পুনরায় মার্জ / সিঙ্ক থ্রেডগুলিতে এমনকি যে পরিমাণ সময় লাগে তা গ্রহণ করে না আরো সময়.
এই ধরণের বিষয়গুলি যেখানে আপনি কোনও গণনার প্রতিটি পদক্ষেপের জন্য থ্রেড রেখেছেন তা কেবল তখনই বোঝা যায় যদি গণনার প্রতিটি পদক্ষেপ @spawn
ওভারহেডের তুলনায় দীর্ঘ সময় নেয় ।
মনে রাখবেন যে ওভারহেডের ওভারহেডকে কমিয়ে আনার কাজ চলছে @spawn
, তবে মাল্টিকোর সিলিকন চিপগুলির খুব পদার্থবিজ্ঞানের দ্বারা আমি সন্দেহ করি যে এটি উপরের fib
প্রয়োগের জন্য কখনও দ্রুত পর্যাপ্ত হতে পারে ।
যদি আপনি কীভাবে আগ্রহী হন যে আমরা কীভাবে থ্রেড fib
ফাংশনটি বাস্তবে উপকারী হতে পারে তা পরিবর্তন করতে পারি, তবে সবচেয়ে সহজ কাজটি কেবল fib
থ্রেডকে ছড়িয়ে দেওয়া হয় যদি আমাদের মনে হয় এটি 1µs
চলার চেয়ে বেশি সময় নেয় take আমার মেশিনে (১ physical টি শারীরিক কোরে চলছে), আমি পেয়েছি
function F(n)
if n < 2
return n
else
return F(n-1)+F(n-2)
end
end
julia> @btime F(23);
122.920 μs (0 allocations: 0 bytes)
সুতরাং একটি থ্রেড তৈরির ব্যয়ের চেয়ে বিশাল দুটি অর্ডারের প্রশংসা করে। এটি ব্যবহার করার জন্য একটি ভাল কাট অফের মতো বলে মনে হচ্ছে:
function fib(n::Int)
if n < 2
return n
elseif n > 23
t = @spawn fib(n - 2)
return fib(n - 1) + fetch(t)
else
return fib(n-1) + fib(n-2)
end
end
এখন, আমি যদি বেঞ্চমার্কটুলস.জেএল [2] এর সাথে সঠিক বেঞ্চমার্ক পদ্ধতি অনুসরণ করি তবে আমার মনে হয়
julia> using BenchmarkTools
julia> @btime fib(43)
971.842 ms (1496518 allocations: 33.64 MiB)
433494437
julia> @btime F(43)
1.866 s (0 allocations: 0 bytes)
433494437
@ আনুশ মন্তব্যগুলিতে জিজ্ঞাসা করেছেন: এটি দেখে মনে হচ্ছে 16 কোর ব্যবহার করে এটি 2 গতি বাড়ানোর একটি কারণ। 16 গতির গতির একটি ফ্যাক্টরের কাছাকাছি কিছু পাওয়া সম্ভব?
হ্যাঁ তাই হয়। উপরের ফাংশনটিতে সমস্যাটি হ'ল ফাংশন বডিটি F
প্রচুর শর্তসাপেক্ষে, ফাংশন / থ্রেড স্পোনিং এবং সমস্ত কিছুর চেয়ে বড় । আমি আপনাকে তুলনা করার জন্য আমন্ত্রণ জানাই @code_llvm F(10)
@code_llvm fib(10)
। এর অর্থ এটি fib
জুলিয়া অনুকূলিতকরণের পক্ষে অনেক বেশি শক্ত। এই অতিরিক্ত ওভারহেড এটি ছোট n
কেসের ক্ষেত্রে পৃথক করে তোলে ।
julia> @btime F(20);
28.844 μs (0 allocations: 0 bytes)
julia> @btime fib(20);
242.208 μs (20 allocations: 320 bytes)
ওহ না! যে সমস্ত অতিরিক্ত কোডের জন্য কখনই স্পর্শ হয় না সেগুলি n < 23
আমাদের প্রশস্ততার ক্রম দ্বারা কমিয়ে দিচ্ছে! যদিও একটি সহজ ফিক্স আছে: কখন n < 23
, একবারে পুনরাবৃত্তি করবেন না fib
, পরিবর্তে একক থ্রেডকে কল করুন F
।
function fib(n::Int)
if n > 23
t = @spawn fib(n - 2)
return fib(n - 1) + fetch(t)
else
return F(n)
end
end
julia> @btime fib(43)
138.876 ms (185594 allocations: 13.64 MiB)
433494437
যা এতগুলি থ্রেডের জন্য আমরা কী প্রত্যাশা করি তার কাছাকাছি ফলাফল দেয়।
[1] https://www.geeksforgeeks.org/time-complexity-recursive-fibonacci-program/
[২] বেঞ্চমার্কটুলস.জেএল @btime
থেকে বেনমার্কটুলস ম্যাক্রো একাধিকবার ফাংশন পরিচালনা করবে, সংকলনের সময় এবং গড় ফলাফলগুলি এড়িয়ে চলে।