এই দুটি পদ্ধতির মধ্যে কোনটি সিতে আরও দক্ষ? এবং কিভাবে সম্পর্কে:
pow(x,3)
বনাম
x*x*x // etc?
এই দুটি পদ্ধতির মধ্যে কোনটি সিতে আরও দক্ষ? এবং কিভাবে সম্পর্কে:
pow(x,3)
বনাম
x*x*x // etc?
উত্তর:
আমি এই কোডটি ব্যবহার করে x*x*...
বনাম pow(x,i)
ছোট মধ্যে পারফরম্যান্স পার্থক্যটি পরীক্ষা করেছি i
:
#include <cstdlib>
#include <cmath>
#include <boost/date_time/posix_time/posix_time.hpp>
inline boost::posix_time::ptime now()
{
return boost::posix_time::microsec_clock::local_time();
}
#define TEST(num, expression) \
double test##num(double b, long loops) \
{ \
double x = 0.0; \
\
boost::posix_time::ptime startTime = now(); \
for (long i=0; i<loops; ++i) \
{ \
x += expression; \
x += expression; \
x += expression; \
x += expression; \
x += expression; \
x += expression; \
x += expression; \
x += expression; \
x += expression; \
x += expression; \
} \
boost::posix_time::time_duration elapsed = now() - startTime; \
\
std::cout << elapsed << " "; \
\
return x; \
}
TEST(1, b)
TEST(2, b*b)
TEST(3, b*b*b)
TEST(4, b*b*b*b)
TEST(5, b*b*b*b*b)
template <int exponent>
double testpow(double base, long loops)
{
double x = 0.0;
boost::posix_time::ptime startTime = now();
for (long i=0; i<loops; ++i)
{
x += std::pow(base, exponent);
x += std::pow(base, exponent);
x += std::pow(base, exponent);
x += std::pow(base, exponent);
x += std::pow(base, exponent);
x += std::pow(base, exponent);
x += std::pow(base, exponent);
x += std::pow(base, exponent);
x += std::pow(base, exponent);
x += std::pow(base, exponent);
}
boost::posix_time::time_duration elapsed = now() - startTime;
std::cout << elapsed << " ";
return x;
}
int main()
{
using std::cout;
long loops = 100000000l;
double x = 0.0;
cout << "1 ";
x += testpow<1>(rand(), loops);
x += test1(rand(), loops);
cout << "\n2 ";
x += testpow<2>(rand(), loops);
x += test2(rand(), loops);
cout << "\n3 ";
x += testpow<3>(rand(), loops);
x += test3(rand(), loops);
cout << "\n4 ";
x += testpow<4>(rand(), loops);
x += test4(rand(), loops);
cout << "\n5 ";
x += testpow<5>(rand(), loops);
x += test5(rand(), loops);
cout << "\n" << x << "\n";
}
ফলাফলগুলি হ'ল:
1 00:00:01.126008 00:00:01.128338
2 00:00:01.125832 00:00:01.127227
3 00:00:01.125563 00:00:01.126590
4 00:00:01.126289 00:00:01.126086
5 00:00:01.126570 00:00:01.125930
2.45829e+54
নোট করুন যে কম্পাইলারটি এটি অপসারণ করে না তা নিশ্চিত করার জন্য আমি প্রতিটি পাও গণনার ফলাফল সংগ্রহ করি।
আমি যদি std::pow(double, double)
সংস্করণটি ব্যবহার করি এবং loops = 1000000l
, আমি পাই:
1 00:00:00.011339 00:00:00.011262
2 00:00:00.011259 00:00:00.011254
3 00:00:00.975658 00:00:00.011254
4 00:00:00.976427 00:00:00.011254
5 00:00:00.973029 00:00:00.011254
2.45829e+52
এটি উন্টু 9.10 64 বিট চলমান একটি ইন্টেল কোর দ্বীপে। জিসিসি ৪.৪.১ ব্যবহার করে -o2 অপ্টিমাইজেশান ব্যবহার করে সংকলিত।
সুতরাং সি তে হ্যাঁ x*x*x
দ্রুত হবে pow(x, 3)
, কারণ কোনও pow(double, int)
ওভারলোড নেই। সি ++ এ এটি প্রায় একই রকম হবে। (আমার পরীক্ষার পদ্ধতিটি সঠিক বলে ধরে নিচ্ছি।)
এটি আন মার্কমের মন্তব্যের জবাবে:
এমনকি যদি একটি using namespace std
নির্দেশ জারি করা হয়, যদি দ্বিতীয় প্যারামিটারটি করার pow
একটি হল int
, তারপর std::pow(double, int)
থেকে জমিদার <cmath>
পরিবর্তে বলা হবে ::pow(double, double)
থেকে <math.h>
।
এই পরীক্ষার কোডটি সেই আচরণটিকে নিশ্চিত করে:
#include <iostream>
namespace foo
{
double bar(double x, int i)
{
std::cout << "foo::bar\n";
return x*i;
}
}
double bar(double x, double y)
{
std::cout << "::bar\n";
return x*y;
}
using namespace foo;
int main()
{
double a = bar(1.2, 3); // Prints "foo::bar"
std::cout << a << "\n";
return 0;
}
std::pow
8 টি লুপের সময় (ক্ষতিকারক> 2 এর জন্য) কল করে, আপনি যদি না ব্যবহার করেন -fno-math-errno
। তারপরে এটি লুপ থেকে পা কলটি টানতে পারে, যেমনটি আমি ভেবেছিলাম। আমার অনুমান যেহেতু এরনো একটি বিশ্বব্যাপী, থ্রেড সুরক্ষার জন্য এটি সম্ভবত পাও কল করতে পারে একাধিকবার ... এর আগেই এক্সপ = 1 এবং এক্সপ = 2 দ্রুত হয় কারণ পাউ কলটি লুপের বাইরে সরিয়ে নেওয়া হয়েছে মাত্র -O3
.. ( সহ - ফিশফ্যাথ-ম্যাথ , এটি লুপের বাইরেও 8-এর যোগফল করে))
pow
লুপ থেকে উত্তোলিত কলটির সাথে ইনলাইন করেছে, সুতরাং সেখানে একটি বড় ত্রুটি রয়েছে। এছাড়াও, দেখে মনে হচ্ছে আপনি বেশিরভাগ এফপি সংযোজনটির বিলম্ব পরীক্ষা করছেন, যেহেতু সমস্ত পরীক্ষা একই পরিমাণে চালিত হয়। আপনি test5
তুলনায় ধীর হতে আশা করতেন test1
, তবে তা হয় না। একাধিক সংযোজক ব্যবহার করে নির্ভরতা শৃঙ্খলা বিভক্ত হবে এবং বিলম্বকে আড়াল করবে।
pow
সর্বদা পরিবর্তিত মান প্রয়োগ করে (বারবার গুঁড়া এক্সপ্রেশনটিকে উত্তোলন থেকে বিরত রাখতে) প্রয়োগ করে আমার বেঞ্চমার্কটি ঠিক করার চেষ্টা করব ।
এটাই ভুল ধরণের প্রশ্ন। সঠিক প্রশ্নটি হবে: "আমার কোডের মানব পাঠকদের পক্ষে কোনটি বোঝা সহজ?"
গতির বিষয়টি যদি (পরে) হয় তবে জিজ্ঞাসা করবেন না, তবে পরিমাপ করুন। (এবং তার আগে, এটি অনুকূলিতকরণ আসলে কোনও লক্ষণীয় পার্থক্য আনবে কিনা তা পরিমাপ করুন)) ততক্ষণ কোডটি লিখুন যাতে এটি পড়া সহজ to
সম্পাদনা করুন
শুধু করতে এই স্পষ্ট (যদিও এটি আগে থেকেই হওয়া উচিত ছিল): সাফল্যদায়ক speedups সাধারণত ভালো জিনিস থেকে আসা ভাল আলগোরিদিম ব্যবহার , ডাটা এলাকার উন্নতি , ডাইনামিক মেমরি ব্যবহার হ্রাস , প্রাক কম্পিউটিং ফলাফল , ইত্যাদি তারা খুব কমই থেকে আসা মাইক্রো-অপ্টিমাইজ করা একক ফাংশন কলগুলি এবং যেখানে তারা করেন তারা খুব কম জায়গায় এটি করেন যা কেবল সতর্কতার সাথে (এবং সময়সাপেক্ষ) প্রোফাইলিংয়ের মাধ্যমে পাওয়া যায়, কখনও কখনও কখনও কখনও খুব অ-স্বজ্ঞাত হয়ে ওঠানো যায় না জিনিস (সন্নিবেশ মত মতnoop
বিবৃতি), এবং একটি প্ল্যাটফর্মের জন্য একটি অপ্টিমাইজেশন হ'ল কখনও কখনও অন্যটির জন্য হতাশার কারণ হয় (এজন্য আপনাকে জিজ্ঞাসার পরিবর্তে আপনাকে পরিমাপ করা দরকার, কারণ আমরা আপনার পরিবেশটি পুরোপুরি জানি না / রাখি না)।
আমাকে আবার এই নিম্নরেখা যাক: এমনকি কয়েক অ্যাপ্লিকেশন যেখানে এই ধরনের জিনিস কোন ব্যাপার, তারা অধিকাংশ জায়গায় তারা ব্যবহার করছি বিষয়টি না, এবং এটা খুব অসম্ভাব্য যে আপনার জায়গায় যেখানে তারা কোড দিকে তাকিয়ে কোন ব্যাপার পাবেন। আপনাকে প্রথমে হট স্পটগুলি চিহ্নিত করার দরকার আছে কারণ অন্যথায় কোডটি অনুকূলকরণ করা কেবল সময়ের অপচয় ।
এমনকি যদি একটি একক ক্রিয়াকলাপ (যেমন কোনও মানের স্কোয়ার গণনা করা) অ্যাপ্লিকেশনটির প্রয়োগের 10% সময় নেয় (যা আইএমই বেশ বিরল), এবং এটি অপ্টিমাইজ করেও সেই অপারেশনের জন্য প্রয়োজনীয় সময়ের 50% সাশ্রয় করে (যা আইএমই হয় এমনকি আরও অনেক বিরল), আপনি এখনও অ্যাপ্লিকেশনটিকে মাত্র 5% কম সময় নিতে পেরেছেন ।
আপনার ব্যবহারকারীদের এমনকি এটি লক্ষ্য করার জন্য একটি স্টপওয়াচ প্রয়োজন। (আমার ধারণা অনুমান যে বেশিরভাগ ক্ষেত্রে ২০% স্পিডআপের নীচে যে কোনও কিছুই বেশিরভাগ ব্যবহারকারীর নজরে আসে না And এবং এটি আপনার সন্ধানের মতো চারটি স্পট))
x*x
বা x*x*x
তার চেয়ে দ্রুততর হবে pow
, যেহেতু pow
সাধারণ ক্ষেত্রে অবশ্যই মোকাবেলা করা উচিত, যেখানে x*x
নির্দিষ্ট। এছাড়াও, আপনি ফাংশন কল এবং এর মতো পছন্দ করতে পারেন।
যাইহোক, আপনি যদি নিজেকে এইভাবে মাইক্রো-অপ্টিমাইজ করে খুঁজে পান তবে আপনাকে প্রোফাইলার পাওয়া এবং কিছু গুরুতর প্রোফাইলিং করা দরকার। অপ্রতিরোধ্য সম্ভাবনা হ'ল আপনি কখনই দুজনের মধ্যে কোনও পার্থক্য লক্ষ্য করবেন না।
x*x*x
বনাম দ্বিগুণ পরীক্ষা করেছি std::pow(double base, int exponent)
এবং একটি পরিসংখ্যানগত অর্থপূর্ণ পারফরম্যান্সের পার্থক্য দেখতে পাচ্ছি না।
আমি পারফরম্যান্স ইস্যু সম্পর্কেও ভাবছিলাম, এবং আশা করছিলাম যে এটি ইমাইলকর্মিয়ার উত্তরটির ভিত্তিতে সংকলকটি অপ্টিমাইজ করবে। তবে আমি ভীত হয়েছি যে তিনি যে পরীক্ষামূলক কোডটি দেখিয়েছিলেন তারপরেও সংকলকটি স্ট্যান্ড :: পাও () কলটি অপ্টিমাইজ করতে দেয়, যেহেতু প্রতিবার কলটিতে একই মান ব্যবহার করা হত, যা সংকলক ফলাফল সংরক্ষণের অনুমতি দেয় এবং এটি লুপে পুনরায় ব্যবহার করুন - এটি সমস্ত ক্ষেত্রে প্রায় একই ধরণের রান-টাইম ব্যাখ্যা করবে। সুতরাং আমি এটি একটি চেহারা ছিল।
আমি যে কোডটি ব্যবহার করেছি তা এখানে (টেস্ট_পো। সিপি):
#include <iostream>
#include <cmath>
#include <chrono>
class Timer {
public:
explicit Timer () : from (std::chrono::high_resolution_clock::now()) { }
void start () {
from = std::chrono::high_resolution_clock::now();
}
double elapsed() const {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - from).count() * 1.0e-6;
}
private:
std::chrono::high_resolution_clock::time_point from;
};
int main (int argc, char* argv[])
{
double total;
Timer timer;
total = 0.0;
timer.start();
for (double i = 0.0; i < 1.0; i += 1e-8)
total += std::pow (i,2);
std::cout << "std::pow(i,2): " << timer.elapsed() << "s (result = " << total << ")\n";
total = 0.0;
timer.start();
for (double i = 0.0; i < 1.0; i += 1e-8)
total += i*i;
std::cout << "i*i: " << timer.elapsed() << "s (result = " << total << ")\n";
std::cout << "\n";
total = 0.0;
timer.start();
for (double i = 0.0; i < 1.0; i += 1e-8)
total += std::pow (i,3);
std::cout << "std::pow(i,3): " << timer.elapsed() << "s (result = " << total << ")\n";
total = 0.0;
timer.start();
for (double i = 0.0; i < 1.0; i += 1e-8)
total += i*i*i;
std::cout << "i*i*i: " << timer.elapsed() << "s (result = " << total << ")\n";
return 0;
}
এটি ব্যবহার করে সংকলিত হয়েছিল:
g++ -std=c++11 [-O2] test_pow.cpp -o test_pow
মূলত, পার্থক্যটি হল std :: Pow () এর আর্গুমেন্ট লুপের পাল্টা। আমি আশঙ্কা হিসাবে, পারফরম্যান্স মধ্যে পার্থক্য উচ্চারিত হয়। -O2 পতাকা ব্যতীত, আমার সিস্টেমে ফলাফলগুলি (আর্চ লিনাক্স 64-বিট, জি ++ 4.9.1, ইন্টেল i7-4930) ছিল:
std::pow(i,2): 0.001105s (result = 3.33333e+07)
i*i: 0.000352s (result = 3.33333e+07)
std::pow(i,3): 0.006034s (result = 2.5e+07)
i*i*i: 0.000328s (result = 2.5e+07)
অপ্টিমাইজেশনের সাথে, ফলাফলগুলি সমানভাবে আকর্ষণীয় ছিল:
std::pow(i,2): 0.000155s (result = 3.33333e+07)
i*i: 0.000106s (result = 3.33333e+07)
std::pow(i,3): 0.006066s (result = 2.5e+07)
i*i*i: 9.7e-05s (result = 2.5e+07)
সুতরাং দেখে মনে হচ্ছে সংকলক কমপক্ষে std :: Pow (x, 2) কেসটি অপ্টিমাইজ করার চেষ্টা করে তবে স্টাড :: পাও (এক্স, 3) কেসটি নয় (এটি স্টাড :: পাওয়ের চেয়ে 40 গুন বেশি সময় নেয়) (x, 2) কেস)। সব ক্ষেত্রে ম্যানুয়াল সম্প্রসারণ আরও ভাল সম্পাদিত হয়েছিল - তবে বিশেষত পাওয়ার 3 কেসের জন্য (60 গুণ দ্রুত)। স্টাড :: পাও () একটি শক্ত লুপে 2 এর চেয়ে বেশি পূর্ণসংখ্যার শক্তি সহ চললে এটি অবশ্যই মনে রাখবেন worth
সর্বাধিক দক্ষ উপায় হ'ল গুণগুলির তাত্পর্যপূর্ণ বৃদ্ধি বিবেচনা করা। পি কোডের জন্য এই কোডটি পরীক্ষা করুন:
template <typename T>
T expt(T p, unsigned q){
T r =1;
while (q != 0) {
if (q % 2 == 1) { // if q is odd
r *= p;
q--;
}
p *= p;
q /= 2;
}
return r;
}
যদি প্রকাশক ধ্রুবক এবং ছোট হয় তবে এটির সংখ্যাটি হ্রাস করুন expand (উদাহরণস্বরূপ, x^4
অনুকূলভাবে নয় x*x*x*x
, তবে y*y
কোথায়y=x*x
আর। x^5
হয় y*y*x
যেখানে y=x*x
ধ্রুব পূর্ণসংখ্যা বহিঃপ্রকাশ জন্য, শুধু ইতিমধ্যে অপ্টিমাইজ ফর্মটি লিখুন এবং তাই।।); ক্ষুদ্র ক্ষয়কারীদের সাথে, এটি একটি প্রমিত অপ্টিমাইজেশন যা কোডটি প্রোফাইল করা হয়েছে কিনা তা করা উচিত। অপ্টিমাইজড ফর্মটি এত বড় শতাংশের মধ্যে দ্রুততর হবে যে এটি মূলত সর্বদা করার মতো।
(আপনি যদি ভিজ্যুয়াল সি ++ ব্যবহার করেন std::pow(float,int)
তবে আমি যে অনুকূলিতকরণটির প্রতিচ্ছবি তৈরি করেছি তা সম্পাদন করে, যার মাধ্যমে অপারেশনগুলির ক্রমটি এক্সপোশনারের বিট প্যাটার্নের সাথে সম্পর্কিত I আমি কোনও গ্যারান্টি দিচ্ছি না যে কম্পাইলারটি আপনার জন্য লুপটি আনরোল করবে, যদিও এটি এখনও করার মতো এটি হাতে।)
[সম্পাদনা] বিটিডব্লিউ- pow
র প্রোফাইলার ফলাফলগুলিতে ক্রপ করার জন্য (আন) বিস্ময়কর প্রবণতা রয়েছে। যদি আপনার একেবারেই এটির প্রয়োজন না হয় (যেমন, এক্সপোনেন্টটি বড় বা ধ্রুবক নয়), এবং আপনি পারফরম্যান্স সম্পর্কে মোটেই উদ্বিগ্ন হন, তবে সর্বোত্তম কোডটি লিখতে ভাল হবে এবং প্রোফাইলার আপনাকে এটি বলার অপেক্ষা রাখবে (আশ্চর্যরূপে ) আরও চিন্তা করার আগে সময় নষ্ট করা। (বিকল্পটি হ'ল ফোন করা pow
এবং প্রোফাইলারকে এটি বলার জন্য বলা (এটি অবাক হওয়ার মতো নয়) সময় নষ্ট করছে - আপনি বুদ্ধি করে এই পদক্ষেপটি কাটাচ্ছেন))
আমি একটি অনুরূপ সমস্যা নিয়ে ব্যস্ত ছিলাম, এবং ফলাফলগুলি দেখে আমি বেশ বিস্মিত। আমি একটি এন-বডি পরিস্থিতিতে নিউটোনীয় মহাকর্ষের জন্য x⁻³ / calc গণনা করছিলাম (দূরত্বের ভেক্টরের a = M G d*(d²)⁻³/²
ডিতে অবস্থিত ভর এম এর অন্য একটি দেহ থেকে ত্বরণ ঘটেছে): (যেখানে ডি ² নিজেই ডি এর বিন্দু (স্কেলার) পণ্য), এবং আমি ভেবেছিলাম গণনা এর M*G*pow(d2, -1.5)
চেয়ে সহজ হবেM*G/d2/sqrt(d2)
কৌশলটি এটি ছোট সিস্টেমগুলির জন্য সত্য, তবে সিস্টেমগুলি আকারে বৃদ্ধি M*G/d2/sqrt(d2)
পাওয়ায় আরও দক্ষ হয়ে ওঠে এবং আমি বুঝতে পারি না কেন সিস্টেমের আকারের ফলে এই ফলাফলটি প্রভাবিত হয়, কারণ বিভিন্ন তথ্যতে অপারেশন পুনরাবৃত্তি করা হয় না। এটি সিস্টেমের বর্ধনের সাথে সাথে সম্ভাব্য অপ্টিমাইজেশন যেমন রয়েছে তবে এটিগুলি সম্ভব নয়pow
x
অবিচ্ছেদ্য বা ফ্লোটিং পয়েন্ট?