c++ অবজ কিভাবে ডি দ্রুত তুলনায় সি++?




সি++ বই (7)

এটি একটি খুব নির্দেশক থ্রেড, OP এবং সহায়কদের সমস্ত কাজ করার জন্য ধন্যবাদ।

এক নোট - এই পরীক্ষাটি বিমূর্ততা / ফিচার পেনাল্টির বা এমনকি বেকেন্ড গুণমানের সাধারণ প্রশ্নটির মূল্যায়ন করা হয় না। এটি কার্যত একটি অপটিমাইজেশন (লুপ অপ্টিমাইজেশান) উপর দৃষ্টি নিবদ্ধ করে। আমি মনে করি ঠিক আছে যে জিপিএ এর ব্যাকএন্ডটি ডিএমডি এর চেয়ে কিছুটা পরিশ্রুত, কিন্তু এটি মনে করা ভুল হবে যে তাদের মধ্যে ফাঁকটি সমস্ত কাজের জন্য বড়।

আমি ডি কিছু বৈশিষ্ট্য পছন্দ, কিন্তু তারা একটি রানটাইম পেনাল্টি সঙ্গে আসা যদি আগ্রহী হবে?

তুলনা করার জন্য, আমি একটি সাধারণ প্রোগ্রাম বাস্তবায়ন করেছি যা C ++ এবং D তে উভয় শর্ট ভেক্টরগুলির স্কলার পণ্যগুলি গণনা করে। ফলাফলটি বিস্ময়কর:

  • ডি: 18.9 গুলি [চূড়ান্ত রানটাইম জন্য নিচে দেখুন]
  • সি ++: 3.8 সেকেন্ড

সি ++ আসলে প্রায় পাঁচবার দ্রুত বা আমি কি ডি প্রোগ্রামে ভুল করেছি?

আমি G ++ -O3 (gcc-snapshot 2011-02-19) এবং DDD-O (dmd 2.052) সহ একটি সাম্প্রতিক সাম্প্রতিক linux ডেস্কটপে C ++ সংকলিত করেছি। ফলাফল অনেক রান এবং মান বিচ্যুতি উপর নগদ।

এখানে সি ++ প্রোগ্রাম:

#include <iostream>
#include <random>
#include <chrono>
#include <string>

#include <vector>
#include <array>

typedef std::chrono::duration<long, std::ratio<1, 1000>> millisecs;
template <typename _T>
long time_since(std::chrono::time_point<_T>& time) {
      long tm = std::chrono::duration_cast<millisecs>( std::chrono::system_clock::now() - time).count();
  time = std::chrono::system_clock::now();
  return tm;
}

const long N = 20000;
const int size = 10;

typedef int value_type;
typedef long long result_type;
typedef std::vector<value_type> vector_t;
typedef typename vector_t::size_type size_type;

inline value_type scalar_product(const vector_t& x, const vector_t& y) {
  value_type res = 0;
  size_type siz = x.size();
  for (size_type i = 0; i < siz; ++i)
    res += x[i] * y[i];
  return res;
}

int main() {
  auto tm_before = std::chrono::system_clock::now();

  // 1. allocate and fill randomly many short vectors
  vector_t* xs = new vector_t [N];
  for (int i = 0; i < N; ++i) {
    xs[i] = vector_t(size);
      }
  std::cerr << "allocation: " << time_since(tm_before) << " ms" << std::endl;

  std::mt19937 rnd_engine;
  std::uniform_int_distribution<value_type> runif_gen(-1000, 1000);
  for (int i = 0; i < N; ++i)
    for (int j = 0; j < size; ++j)
      xs[i][j] = runif_gen(rnd_engine);
  std::cerr << "random generation: " << time_since(tm_before) << " ms" << std::endl;

  // 2. compute all pairwise scalar products:
  time_since(tm_before);
  result_type avg = 0;
  for (int i = 0; i < N; ++i)
    for (int j = 0; j < N; ++j) 
      avg += scalar_product(xs[i], xs[j]);
  avg = avg / N*N;
  auto time = time_since(tm_before);
  std::cout << "result: " << avg << std::endl;
  std::cout << "time: " << time << " ms" << std::endl;
}

এবং এখানে ডি সংস্করণ:

import std.stdio;
import std.datetime;
import std.random;

const long N = 20000;
const int size = 10;

alias int value_type;
alias long result_type;
alias value_type[] vector_t;
alias uint size_type;

value_type scalar_product(const ref vector_t x, const ref vector_t y) {
  value_type res = 0;
  size_type siz = x.length;
  for (size_type i = 0; i < siz; ++i)
    res += x[i] * y[i];
  return res;
}

int main() {   
  auto tm_before = Clock.currTime();

  // 1. allocate and fill randomly many short vectors
  vector_t[] xs;
  xs.length = N;
  for (int i = 0; i < N; ++i) {
    xs[i].length = size;
  }
  writefln("allocation: %i ", (Clock.currTime() - tm_before));
  tm_before = Clock.currTime();

  for (int i = 0; i < N; ++i)
    for (int j = 0; j < size; ++j)
      xs[i][j] = uniform(-1000, 1000);
  writefln("random: %i ", (Clock.currTime() - tm_before));
  tm_before = Clock.currTime();

  // 2. compute all pairwise scalar products:
  result_type avg = cast(result_type) 0;
  for (int i = 0; i < N; ++i)
    for (int j = 0; j < N; ++j) 
      avg += scalar_product(xs[i], xs[j]);
  avg = avg / N*N;
  writefln("result: %d", avg);
  auto time = Clock.currTime() - tm_before;
  writefln("scalar products: %i ", time);

  return 0;
}

নিশ্চিতভাবে একটি মানের-বাস্তবায়ন সমস্যা মত মনে হয়।

আমি OP এর কোড দিয়ে কিছু পরীক্ষা চালানো এবং কিছু পরিবর্তন করেছেন। আমি আসলে এলডিসি / ক্ল্যাং ++ এর জন্য দ্রুততর ডি পেয়ে যাচ্ছি , এই ধারণাটি পরিচালনা করে যে অ্যারেগুলি গতিশীলভাবে ( xs এবং সংশ্লিষ্ট স্কলার) বরাদ্দ করা আবশ্যক । কিছু সংখ্যক জন্য নিচে দেখুন।

OP জন্য প্রশ্ন

এটা কি ইচ্ছাকৃত যে একই বীজ সি ++ প্রতিটি পুনরাবৃত্তি জন্য ব্যবহার করা হয়, যখন ডি তাই না?

সেটআপ

প্ল্যাটফর্মের মধ্যে এটি পোর্টেবল করতে আমি মূল ডি উত্স ( scalar.d ) scalar.d । এটি শুধুমাত্র অ্যারের আকার অ্যাক্সেস এবং সংশোধন করতে ব্যবহৃত নম্বরগুলির ধরন পরিবর্তন করে।

এর পর, আমি নিম্নলিখিত পরিবর্তনগুলি তৈরি করেছি:

  • Xs মধ্যে scalars জন্য ডিফল্ট inits এড়ানোর জন্য uninitializedArray ব্যবহৃত (সম্ভবত বৃহত্তম পার্থক্য তৈরি)। এটি গুরুত্বপূর্ণ কারণ D সাধারণভাবে ডিফল্ট-সব কিছু চুপ করে থাকে, যা C ++ হয় না।

  • প্রিন্টিং কোড writefln এবং writefln সঙ্গে writeln

  • পরিবর্তনশীল হতে পরিবর্তন আমদানি
  • গণনা গড়ের চূড়ান্ত পদক্ষেপের জন্য ম্যানুয়াল গুণের পরিবর্তে ব্যবহৃত পা চালক ( ^^ )
  • size_type সরানো হয়েছে এবং নতুন index_type উপনাম সহ যথাযথভাবে প্রতিস্থাপিত হয়েছে

... এইভাবে scalar2.cpp ( pastebin ) ফলে:

    import std.stdio : writeln;
    import std.datetime : Clock, Duration;
    import std.array : uninitializedArray;
    import std.random : uniform;

    alias result_type = long;
    alias value_type = int;
    alias vector_t = value_type[];
    alias index_type = typeof(vector_t.init.length);// Make index integrals portable - Linux is ulong, Win8.1 is uint

    immutable long N = 20000;
    immutable int size = 10;

    // Replaced for loops with appropriate foreach versions
    value_type scalar_product(in ref vector_t x, in ref vector_t y) { // "in" is the same as "const" here
      value_type res = 0;
      for(index_type i = 0; i < size; ++i)
        res += x[i] * y[i];
      return res;
    }

    int main() {
      auto tm_before = Clock.currTime;
      auto countElapsed(in string taskName) { // Factor out printing code
        writeln(taskName, ": ", Clock.currTime - tm_before);
        tm_before = Clock.currTime;
      }

      // 1. allocate and fill randomly many short vectors
      vector_t[] xs = uninitializedArray!(vector_t[])(N);// Avoid default inits of inner arrays
      for(index_type i = 0; i < N; ++i)
        xs[i] = uninitializedArray!(vector_t)(size);// Avoid more default inits of values
      countElapsed("allocation");

      for(index_type i = 0; i < N; ++i)
        for(index_type j = 0; j < size; ++j)
          xs[i][j] = uniform(-1000, 1000);
      countElapsed("random");

      // 2. compute all pairwise scalar products:
      result_type avg = 0;
      for(index_type i = 0; i < N; ++i)
        for(index_type j = 0; j < N; ++j)
          avg += scalar_product(xs[i], xs[j]);
      avg /= N ^^ 2;// Replace manual multiplication with pow operator
      writeln("result: ", avg);
      countElapsed("scalar products");

      return 0;
    }

scalar2.d (যা স্পিডের জন্য অপ্টিমাইজেশান অগ্রাধিকার দেয়) পরীক্ষা করার পরে, scalar2.d বাইরে আমি main scalar3.d foreach সমতুল্য দিয়ে প্রতিস্থাপিত করে এবং এটি scalar3.d ( pastebin ) বলা হয়:

    import std.stdio : writeln;
    import std.datetime : Clock, Duration;
    import std.array : uninitializedArray;
    import std.random : uniform;

    alias result_type = long;
    alias value_type = int;
    alias vector_t = value_type[];
    alias index_type = typeof(vector_t.init.length);// Make index integrals portable - Linux is ulong, Win8.1 is uint

    immutable long N = 20000;
    immutable int size = 10;

    // Replaced for loops with appropriate foreach versions
    value_type scalar_product(in ref vector_t x, in ref vector_t y) { // "in" is the same as "const" here
      value_type res = 0;
      for(index_type i = 0; i < size; ++i)
        res += x[i] * y[i];
      return res;
    }

    int main() {
      auto tm_before = Clock.currTime;
      auto countElapsed(in string taskName) { // Factor out printing code
        writeln(taskName, ": ", Clock.currTime - tm_before);
        tm_before = Clock.currTime;
      }

      // 1. allocate and fill randomly many short vectors
      vector_t[] xs = uninitializedArray!(vector_t[])(N);// Avoid default inits of inner arrays
      foreach(ref x; xs)
        x = uninitializedArray!(vector_t)(size);// Avoid more default inits of values
      countElapsed("allocation");

      foreach(ref x; xs)
        foreach(ref val; x)
          val = uniform(-1000, 1000);
      countElapsed("random");

      // 2. compute all pairwise scalar products:
      result_type avg = 0;
      foreach(const ref x; xs)
        foreach(const ref y; xs)
          avg += scalar_product(x, y);
      avg /= N ^^ 2;// Replace manual multiplication with pow operator
      writeln("result: ", avg);
      countElapsed("scalar products");

      return 0;
    }

আমি এলএলভিভি-ভিত্তিক কম্পাইলার ব্যবহার করে এই প্রতিটি পরীক্ষা সংকলন করেছি, কারণ এলডিসি কার্য সম্পাদনের ক্ষেত্রে ডি সংকলনের জন্য সর্বোত্তম বিকল্প বলে মনে হচ্ছে। আমার x86_64 আর্কি লিনাক্স ইনস্টলেশনের উপর আমি নিম্নলিখিত প্যাকেজগুলি ব্যবহার করেছি:

  • clang 3.6.0-3
  • ldc 1:0.15.1-4
  • dtools 2.067.0-2

আমি প্রতিটি কম্পাইল করার জন্য নিম্নলিখিত কমান্ড ব্যবহার করেছি:

  • C ++: clang++ scalar.cpp -o"scalar.cpp.exe" -std=c++11 -O3
  • ডি: rdmd --compiler=ldc2 -O3 -boundscheck=off <sourcefile>

ফলাফল

উৎসের প্রতিটি সংস্করণের ফলাফলগুলি ( কাঁচা কনসোল আউটপুটের স্ক্রিনশট ) নিম্নরূপ:

  1. scalar.cpp (মূল C ++):

    allocation: 2 ms
    
    random generation: 12 ms
    
    result: 29248300000
    
    time: 2582 ms

    সি ++ 2582 মিঃ এ মান নির্ধারণ করে।

  2. scalar.d (পরিবর্তনশীল ওপ উৎস):

    allocation: 5 ms, 293 μs, and 5 hnsecs 
    
    random: 10 ms, 866 μs, and 4 hnsecs 
    
    result: 53237080000
    
    scalar products: 2 secs, 956 ms, 513 μs, and 7 hnsecs 

    এই ~ 2957 মিঃ জন্য দৌড়ে। সি ++ বাস্তবায়ন চেয়ে ধীর, কিন্তু অত্যধিক নয়।

  3. scalar2.d (সূচী / দৈর্ঘ্য টাইপ পরিবর্তন এবং scalar2.d অপ্টিমাইজেশান):

    allocation: 2 ms, 464 μs, and 2 hnsecs
    
    random: 5 ms, 792 μs, and 6 hnsecs
    
    result: 59
    
    scalar products: 1 sec, 859 ms, 942 μs, and 9 hnsecs

    অন্য কথায়, ~ 1860 মি । এতদূর এই সীসা হয়।

  4. scalar3.d (foreaches):

    allocation: 2 ms, 911 μs, and 3 hnsecs
    
    random: 7 ms, 567 μs, and 8 hnsecs
    
    result: 189
    
    scalar products: 2 secs, 182 ms, and 366 μs

    ~ 2182 ms scalar2.d চেয়েও ধীর, কিন্তু C ++ সংস্করণের চেয়ে দ্রুত।

উপসংহার

সঠিক অপটিমাইজেশনগুলির সাথে, ডি বাস্তবায়নটি এলএলভিএম-ভিত্তিক কম্পাইলারগুলি ব্যবহার করে তার সমতুল্য সি ++ বাস্তবায়নের চেয়ে দ্রুত গতিতে চলেছে। অধিকাংশ অ্যাপ্লিকেশনের জন্য ডি এবং সি ++ এর মধ্যে বর্তমান ফাঁক বর্তমান প্রয়োগগুলির সীমাবদ্ধতার উপর ভিত্তি করেই প্রদর্শিত হয়।


বাস্তবায়ন সমস্যা একটি মানের মত মনে হয়। উদাহরণস্বরূপ, এখানে আমি যা পরীক্ষা করেছি তা হল:

import std.datetime, std.stdio, std.random;

version = ManualInline;

immutable N = 20000;
immutable Size = 10;

alias int value_type;
alias long result_type;
alias value_type[] vector_type;

result_type scalar_product(in vector_type x, in vector_type y)
in
{
    assert(x.length == y.length);
}
body
{
    result_type result = 0;

    foreach(i; 0 .. x.length)
        result += x[i] * y[i];

    return result;
}

void main()
{   
    auto startTime = Clock.currTime();

    // 1. allocate vectors
    vector_type[] vectors = new vector_type[N];
    foreach(ref vec; vectors)
        vec = new value_type[Size];

    auto time = Clock.currTime() - startTime;
    writefln("allocation: %s ", time);
    startTime = Clock.currTime();

    // 2. randomize vectors
    foreach(ref vec; vectors)
        foreach(ref e; vec)
            e = uniform(-1000, 1000);

    time = Clock.currTime() - startTime;
    writefln("random: %s ", time);
    startTime = Clock.currTime();

    // 3. compute all pairwise scalar products
    result_type avg = 0;

    foreach(vecA; vectors)
        foreach(vecB; vectors)
        {
            version(ManualInline)
            {
                result_type result = 0;

                foreach(i; 0 .. vecA.length)
                    result += vecA[i] * vecB[i];

                avg += result;
            }
            else
            {
                avg += scalar_product(vecA, vecB);
            }
        }

    avg = avg / (N * N);

    time = Clock.currTime() - startTime;
    writefln("scalar products: %s ", time);
    writefln("result: %s", avg);
}

ManualInline সংজ্ঞায়িত করে আমি 28 সেকেন্ড পাই, কিন্তু আমি 32 পেতে পারিনি। সুতরাং কম্পাইলার এই সহজ ফাংশনটিও ManualInline করছে না, যা আমি মনে করি এটি পরিষ্কার হওয়া উচিত।

(আমার কমান্ড লাইনটি dmd -O -noboundscheck -inline -release ... )


সমস্ত অপটিমাইজেশন সক্ষম এবং সমস্ত সুরক্ষা চেক নিষ্ক্রিয় করতে, নিম্নলিখিত ডিএমডি পতাকা দিয়ে আপনার ডি প্রোগ্রাম কম্পাইল করুন:

-O -inline -release -noboundscheck

সম্পাদনা করুন : আমি আপনার প্রোগ্রামগুলি g ++, dmd এবং gdc দিয়ে চেষ্টা করেছি। dmd পিছনে পিছনে না, কিন্তু gdc g ++ খুব কাছাকাছি কর্মক্ষমতা অর্জন। আমি ব্যবহৃত কমান্ড লাইন gdmd -O -release -inline -release- gdmd -O -release -inline (gdmd ডিডিডি অপশন গ্রহণ করে যা gdc এর চারপাশে একটি মোড়ক)।

এসেম্বলার তালিকাটি দেখতে, এটি dmd এবং gdc ইনলাইন করা হয়নি scalar_product , কিন্তু g ++ / gdc এমএমএক্স নির্দেশগুলি নির্বাহ করেছে, তাই তারা লুপটি স্বয়ংক্রিয়-ভেক্টরাইজ করা হতে পারে।


dmd ভাষাটির রেফারেন্স বাস্তবায়ন এবং এইভাবে সর্বাধিক কাজটি ব্যাকএন্ডটি অপ্টিমাইজ করার পরিবর্তে বাগগুলি ঠিক করার জন্য ফ্রন্টেন্ডে রাখা হয়।

"ইন" আপনার ক্ষেত্রে দ্রুত কারণ আপনি রেফারেন্স ধরনের যা গতিশীল অ্যারে ব্যবহার করা হয়। রেফারেন্সের সাথে আপনি অন্য স্তরটির পরিমাপ পরিচয় করান (যা সাধারণত অ্যারেকে পরিবর্তন করতে এবং শুধুমাত্র সামগ্রীগুলি পরিবর্তন করতে ব্যবহৃত হয়)।

ভেক্টর সাধারণত structs সঙ্গে প্রয়োগ করা হয় যেখানে কন্স রেফার নিখুঁত জ্ঞান করে তোলে। ভেক্টর অপারেশন এবং smallpt লোড সমন্বিত একটি বাস্তব বিশ্বের উদাহরণ জন্য smallptD বনাম smallpt দেখুন।

মনে রাখবেন যে 64-বিট এছাড়াও একটি পার্থক্য করতে পারে। আমি একবার x64 gcc এ 64-বিট কোড কম্পাইল করে ফেলেছি যখন dmd এখনও ডিফল্ট 32 (64-বিট কোডজেন মাপের সময় পরিবর্তন করবে)। "Dmd -m64 ..." এর সাথে একটি অসাধারণ স্পিডআপ ছিল।


ডি নিচে slows যে একটি বড় জিনিস একটি subpar আবর্জনা সংগ্রহ বাস্তবায়ন। জি-সি চাপিয়ে দেয় না এমন বেঞ্চমার্কগুলি একই কম্পাইলার ব্যাকএন্ডের সাথে সংকলিত সি এবং সি ++ কোডের অনুরূপ কর্মক্ষমতা প্রদর্শন করবে। বেঞ্চমার্কগুলি গুরুতরভাবে জিসি-তে চাপিয়ে দেয় তা দেখায় যে ডি অস্বস্তিকরভাবে সঞ্চালিত হয়। বিশ্রাম নিশ্চিত, যদিও, এটি একটি একক (গুরুতর) গুণমানের বাস্তবায়ন সমস্যা, নষ্ট হয়ে যাওয়া গ্যারান্টি নয়। এছাড়াও, ডিটি আপনাকে GC থেকে বাদ দেওয়ার এবং কর্মক্ষমতা-সমালোচনামূলক বিটগুলিতে মেমরি পরিচালনার সুরক্ষার ক্ষমতা দেয়, যদিও এটি এখনও কম কার্য সম্পাদন-আপনার কোডের 95% গুরুতর ব্যবহারে ব্যবহার করে।

আমি সাম্প্রতিক সময়ে জিসি কর্মক্ষমতা উন্নত করার জন্য কিছু প্রচেষ্টা করেছি এবং ফলাফলটি বরং কমপক্ষে সিন্থেটিক benchmarks, বরং নাটকীয় হয়েছে। আশা করি এই পরিবর্তনগুলি পরবর্তী কয়েকটি রিলিজগুলির মধ্যে একত্রিত হবে এবং সমস্যাটিকে হ্রাস করবে।


সি ++ বা ডিটি দ্রুত কিনা তা আপনি যা করছেন তা অত্যন্ত নির্ভরশীল হতে পারে। আমি মনে করি ভাল লেখা লিখিত ডি ++ কোডটি ভালভাবে লিখিত ডি কোডের সাথে তুলনা করার সময়, সাধারণত এটি একই গতির হবে, বা সি ++ দ্রুততর হবে তবে বিশেষ কম্পাইলারটি কীভাবে অপ্টিমাইজেশান পরিচালনা করে সেটি ভাষা থেকে সম্পূর্ণরূপে প্রভাব ফেলতে পারে নিজেই।

যাইহোক, ডি কয়েকটি ক্ষেত্রে রয়েছে যেখানে ডি স্পিডের জন্য C ++ মারার একটি ভাল সুযোগ রয়েছে। মন আসে যা প্রধান এক স্ট্রিং প্রসেসিং হবে। ধন্যবাদ ডি'এর অ্যারের স্লাস্টিং capabalities, স্ট্রিং (এবং সাধারণভাবে অ্যারে) আপনি সহজে সি ++ করতে পারেন তুলনায় অনেক দ্রুত প্রক্রিয়া করা যেতে পারে। D1 এর জন্য, ট্যাঙ্গো এর এক্সএমএল প্রসেসরটি অত্যন্ত দ্রুত , ধন্যবাদ প্রধানত ডি'স অ্যারের স্লাস্টিংয়ের ক্ষমতার জন্য (এবং আশা করি D2 এর সমতুল্য দ্রুত এক্সএমএল প্যাসারার থাকবে যা ফোবসের জন্য বর্তমানে কাজ করা হচ্ছে)। সুতরাং, শেষ পর্যন্ত ডি বা সি ++ দ্রুত হতে চলেছে কিনা তা আপনি যা করছেন তার উপর নির্ভরশীল হতে চলেছে।

এখন, আমি সুপারিশ করছি যে আপনি এই বিশেষ ক্ষেত্রে গতিতে এই রকম পার্থক্য দেখছেন, কিন্তু এটি এমন একটি জিনিস যা আমি উন্নত করার আশা করি যেমন dmd উন্নতি করে। Gdc ব্যবহার করে আরও ভাল ফলাফল পেতে পারে এবং এটি সম্ভবত গিগাবাইট ভিত্তিক ভাষাটির সাথে তুলনামূলকভাবে তুলনামূলকভাবে তুলনামূলকভাবে তুলনা করবে। কিন্তু ডিএমডি জেনারেট করে এমন কোডটি দ্রুততর করার জন্য অনেক কিছু করতে গেলে এটি আমাকে অবাক করবে না। আমি এই মুহুর্তে dmd চেয়ে gcc আরো পরিপক্ক যে অনেক প্রশ্ন আছে মনে হয় না। এবং কোড অপ্টিমাইজেশন কোড মেয়াদপূর্তির প্রধান ফল।

অবশেষে, ডিএমডি আপনার বিশেষ অ্যাপ্লিকেশনের জন্য কতটা ভাল কাজ করে তা গুরুত্বপূর্ণ, তবে আমি সাধারণভাবে C ++ এবং D এর তুলনামূলকভাবে তুলনা করা কতটা ভাল তা জানতে পারব। তত্ত্বের মধ্যে, তারা বেশিরভাগ একই হওয়া উচিত, তবে এটি বাস্তবায়নের উপর নির্ভর করে। আমি মনে করি যে বেঞ্চমার্কগুলির একটি বিস্তৃত সেটটি প্রকৃতপক্ষে দুটি তুলনায় কতটা ভালভাবে পরীক্ষা করবে তা যাচাই করতে হবে।





d