সি ইন অ্যারে শূন্যে রিসেট করুন: দ্রুততম উপায়?


102

ধরে নিই যে আমাদের T myarray[100]টি = ইনট, স্বাক্ষরবিহীন ইনট, লং লং ইনট বা স্বাক্ষরযুক্ত লং লং ইন্টের সাথে একটি রয়েছে, এর সমস্ত সামগ্রী শূন্যে রিসেট করার দ্রুততম উপায় কী (কেবলমাত্র আরম্ভের জন্য নয় তবে আমার প্রোগ্রামটিতে বেশ কয়েকবার সামগ্রী পুনরায় সেট করার জন্য) ? মেমসেটের সাথে থাকতে পারে?

গতিশীল অ্যারের মতো একই প্রশ্ন T *myarray = new T[100]


16
@BoPersson: ভাল, new হয় সি ++ ...
Matteo ইতালিয়া

@ মাট্টিও - ভাল, হ্যাঁ উত্তরগুলি খুব বেশি প্রভাবিত করেনি (এখন অবধি :-) পর্যন্ত।
বো পারসন

3
@ বুপারসন: আমি memsetযখন সি ++ এর সাথে কোনওভাবে জড়িত তখনই আমার কথা বলতে খারাপ লাগছিল ... :)
মাত্তেও ইতালি

2
একটি আধুনিক সংকলকটিতে, আপনি একটি সাধারণ forলুপটি বীট করতে পারবেন না । তবে আশ্চর্যজনকভাবে আপনি স্মার্ট হওয়ার চেষ্টা করে অনেক খারাপ কাজ করতে পারেন।
ডেভিড শোয়ার্জ

একটি কাঠামো ব্যবহার করুন এবং এটির ভিতরে একটি অ্যারের কাঠি করুন। সমস্ত শূন্য একটি উদাহরণ তৈরি করুন। আপনার তৈরি অন্যদের শূন্য করতে এটি ব্যবহার করুন। এটা ভাল কাজ করে. অন্তর্ভুক্ত নেই, কোনও কার্য নেই, বেশ দ্রুত।
Xofo

উত্তর:


170

memset(থেকে <string.h>) সম্ভবত দ্রুততম স্ট্যান্ডার্ড উপায়, যেহেতু এটি সাধারণত নিয়মিতভাবে সমাবেশে রচনা এবং হাত দ্বারা অনুকূলিতকরণ।

memset(myarray, 0, sizeof(myarray)); // for automatically-allocated arrays
memset(myarray, 0, N*sizeof(*myarray)); // for heap-allocated arrays, where N is the number of elements

যাইহোক, সি ++ এ idiomatic উপায়টি ব্যবহার করা হবে std::fill(থেকে <algorithm>):

std::fill(myarray, myarray+N, 0);

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


10
১৯৯ ISO এর আইএসও সি স্ট্যান্ডার্ড হিসাবে এটির গ্যারান্টিটি দেওয়া হয়নি যে memsetএটি একটি পূর্ণসংখ্যা 0 তে নির্ধারণ করবে; অল-বিট-শূন্য একটি উপস্থাপনা যে কোনও নির্দিষ্ট বিবৃতি ছিল না 0। একটি টেকনিক্যাল কোরিয়েনডাম এমন গ্যারান্টি যুক্ত করেছে, যা ২০১১ এর আইএসও সি স্ট্যান্ডার্ডের অন্তর্ভুক্ত। আমি বিশ্বাস করি যে সব বিট-জিরো হয় একটি বৈধ উপস্থাপনা 0সমস্ত বিদ্যমান সি এবং সি ++ বাস্তবায়নের মধ্যে সব ধরনের পূর্ণসংখ্যা, যা কেন কমিটি যে প্রয়োজন যোগ করতে পারবেন থেকেই আছেন। ' (ভাসমান-পয়েন্ট বা পয়েন্টার ধরণের কোনও অনুরূপ গ্যারান্টি নেই is)
কিথ থম্পসন

3
@ কিথথম্পসনের মন্তব্যে যোগ করা: এই গ্যারান্টিটি টিসি 2 (2004) এর সরল পাঠ্যে 6.2.6.2/5 এ যুক্ত করা হয়েছিল; তবে যদি কোনও প্যাডিং বিট না থাকে তবে 6.2.6.2/1 এবং / 2 ইতিমধ্যে গ্যারান্টিযুক্ত যে সমস্ত বিট-শূন্য ছিল 0। (প্যাডিং বিটের সাহায্যে সম্ভাবনা বিদ্যমান যে অল-বিট-শূন্য একটি ফাঁদ উপস্থাপনা হতে পারে)। তবে যে কোনও ক্ষেত্রে, টিসির ত্রুটিযুক্ত পাঠ্যটি স্বীকৃতি দেওয়া এবং প্রতিস্থাপন করার কথা রয়েছে, সুতরাং ২০০৪ সাল পর্যন্ত আমাদের এমন আচরণ করা উচিত যেন সি 99 এ সবসময় এই লেখাটি রাখে।
এমএম

সি তে, আপনি যদি ডায়নামিক অ্যারেটি সঠিকভাবে বরাদ্দ করেন তবে দুটি স্মৃতিতে কোনও পার্থক্য থাকবে না। সঠিক গতিশীল বরাদ্দ হবে int (*myarray)[N] = malloc(sizeof(*myarray));
লন্ডিন

@ লন্ডিন: অবশ্যই - আপনি যদি সংকলন সময়ে জানেন যে কতটা বড় Nতবে আপনি যদি বেশিরভাগ ক্ষেত্রেই ব্যবহার করেন mallocআপনি কেবল রানটাইমটিতে জানতেন।
মাত্তেও ইটালিয়া

@ মাট্টিও ইটালিয়া ১৯৯৯ সাল থেকে আমাদের ভিএলএস রয়েছে।
লন্ডিন

20

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

মনে রাখবেন যে এই সমাধানটি জেনেরিক নয়, এটি কেবল 32 বা 64 বিটের ডেটাতে কাজ করে। দয়া করে মন্তব্য করুন যদি এই কোডটি কিছু ভুল করছে।

#include<immintrin.h>
#define intrin_ZERO(a,n){\
size_t x = 0;\
const size_t inc = 32 / sizeof(*(a));/*size of 256 bit register over size of variable*/\
for (;x < n-inc;x+=inc)\
    _mm256_storeu_ps((float *)((a)+x),_mm256_setzero_ps());\
if(4 == sizeof(*(a))){\
    switch(n-x){\
    case 3:\
        (a)[x] = 0;x++;\
    case 2:\
        _mm_storeu_ps((float *)((a)+x),_mm_setzero_ps());break;\
    case 1:\
        (a)[x] = 0;\
        break;\
    case 0:\
        break;\
    };\
}\
else if(8 == sizeof(*(a))){\
switch(n-x){\
    case 7:\
        (a)[x] = 0;x++;\
    case 6:\
        (a)[x] = 0;x++;\
    case 5:\
        (a)[x] = 0;x++;\
    case 4:\
        _mm_storeu_ps((float *)((a)+x),_mm_setzero_ps());break;\
    case 3:\
        (a)[x] = 0;x++;\
    case 2:\
        ((long long *)(a))[x] = 0;break;\
    case 1:\
        (a)[x] = 0;\
        break;\
    case 0:\
        break;\
};\
}\
}

আমি দাবি করব না যে এটি সবচেয়ে দ্রুত পদ্ধতি, যেহেতু আমি নিম্ন স্তরের অপ্টিমাইজেশান বিশেষজ্ঞ নই। বরং এটি একটি সঠিক আর্কিটেকচার নির্ভর বাস্তবায়নের একটি উদাহরণ যা মেমসেটের চেয়ে দ্রুত।

এখন, ফলাফলের দিকে। আমি স্ট্যাটিক এবং ডায়নামিকভাবে উভয়ই বরাদ্দকৃত আকারের 100 ইন্টি এবং দীর্ঘ দীর্ঘ অ্যারেগুলির জন্য পারফরম্যান্স গণনা করেছি, তবে এমএসভিসি ব্যতীত, যা স্ট্যাটিক অ্যারেগুলিতে একটি ডেড কোড নির্মূল করেছিল, ফলাফলগুলি অত্যন্ত তুলনীয় ছিল, তাই আমি কেবলমাত্র গতিশীল অ্যারে পারফরম্যান্স দেখাব। টাইম হ্যাঙ্কের নিম্ন নির্ভুলতা ঘড়ি ফাংশনটি ব্যবহার করে সময় চিহ্নিতকরণগুলি 1 মিলিয়ন পুনরাবৃত্তির জন্য এমএস।

ঝাঁকুনি ৩.৮ (ক্ল্যাং-ক্লার ফ্রন্টএন্ড ব্যবহার করে, অপ্টিমাইজেশান পতাকাগুলি = / ওএক্স / আর্চ: এভিএক্স / ওআই / ওটি)

int:
memset:      99
fill:        97
ZERO:        98
intrin_ZERO: 90

long long:
memset:      285
fill:        286
ZERO:        285
intrin_ZERO: 188

জিসিসি 5.1.0 (অপ্টিমাইজেশান ফ্ল্যাগস: -ও 3 -মার্চ = নেটিভ -মিটিউন = নেটিভ-ম্যাকএক্স):

int:
memset:      268
fill:        268
ZERO:        268
intrin_ZERO: 91
long long:
memset:      402
fill:        399
ZERO:        400
intrin_ZERO: 185

এমএসভিসি 2015 (অপ্টিমাইজেশন পতাকা: / ওএক্স / খিলান: এভিএক্স / ওআই / ওটি):

int
memset:      196
fill:        613
ZERO:        221
intrin_ZERO: 95
long long:
memset:      273
fill:        559
ZERO:        376
intrin_ZERO: 188

এখানে অনেক আকর্ষণীয় কাজ চলছে: এলএলভিএম কিলিং জিসিসি, এমএসভিসির টিপিকাল স্পটটি অপটিমাইজেশন (এটি স্ট্যাটিক অ্যারেগুলিতে একটি চিত্তাকর্ষক ডেড কোড নির্মূল করে এবং তারপরে পূরণের জন্য ভয়াবহ কর্মক্ষমতা রয়েছে) has যদিও আমার বাস্তবায়নটি তাত্পর্যপূর্ণভাবে দ্রুত, তবে এটি কেবল কারণ এটির স্বীকৃতি রয়েছে যে অন্য কোনও সেটিং অপারেশন থেকে বিট ক্লিয়ারিংয়ের ওভারহেড অনেক কম।

ক্ল্যাংয়ের বাস্তবায়নটি আরও তাত্পর্যপূর্ণভাবে দেখার যোগ্য, কারণ এটি উল্লেখযোগ্যভাবে দ্রুত। কিছু অতিরিক্ত টেস্টিং দেখায় যে এর মেমসেটটি আসলে শূন্যের জন্য বিশেষায়িত - 400 বাইট অ্যারের জন্য নন শূন্য মেমসেটগুলি অনেক ধীর (~ 220 মিমি) এবং গিসি এর সাথে তুলনীয়। যাইহোক, 800 বাইট অ্যারের সাথে ননজারো মেমসেটিংয়ের গতির কোনও পার্থক্য নেই, সম্ভবত এই কারণেই তাদের মেমসেটটি আমার বাস্তবায়নের চেয়ে খারাপ পারফরম্যান্স করেছে - বিশেষায়িতকরণটি কেবলমাত্র ছোট অ্যারেগুলির জন্য এবং কাটটফটি প্রায় 800 বাইটের কাছাকাছি। আরও মনে রাখবেন যে জিসিসি 'ফিল' এবং 'জিরো' মেমসেটটিকে উত্সাহিত করছে না (উত্পন্ন কোডটি দেখছে), জিসিসি কেবল অভিন্ন পারফরম্যান্সের বৈশিষ্ট্য সহ কোড উত্পন্ন করছে।

উপসংহার: মেমসেটটি এই কাজের জন্য সত্যই অনুকূলিত হয় না এবং পাশাপাশি লোকেরা এটি ভান করে (অন্যথায় জিসিসি এবং এমএসভিসি এবং এলএলভিএমের মেমসেটের একই কার্যকারিতা থাকে)। যদি পারফরম্যান্সের বিষয়টি বিবেচনা করে তবে মেমসেটটি একটি চূড়ান্ত সমাধান হওয়া উচিত নয়, বিশেষত এই বিশ্রী মাঝারি আকারের অ্যারেগুলির জন্য, কারণ এটি বিট সাফ করার জন্য বিশেষীকরণ করা হয় না এবং সংকলকটি নিজে থেকে এটি করতে পারে তার চেয়ে ভাল এটি কোনও অনুকূল নয় optim


4
কোড ছাড়াই এবং সংকলক সংস্করণ এবং ব্যবহৃত বিকল্পগুলির উল্লেখ ছাড়াই একটি বেঞ্চমার্ক? হুম ...
মার্ক গ্লিস

আমার কাছে ইতিমধ্যে সংকলক সংস্করণ ছিল (সেগুলি কেবল কিছুটা গোপন ছিল), এবং ব্যবহৃত প্রযোজ্য বিকল্পগুলি জুড়েছে।
বেনিয়ামিন

অকেইরির অবৈধ প্রকারের আর্গুমেন্ট '*' ('সাইজ_ট {ওরফে স্বাক্ষরবিহীন ইন্ট।' আছে) |
পাইওটার ওয়াসেলিউইচজ

আপনার নিজের অনুকূলিত শূন্য পদ্ধতিটি লিখতে এত উদার হওয়া - আপনি কীভাবে এটি কাজ করে তার উপর কয়েকটি শব্দ রেখে দিতে পারেন, এবং কেন এটি দ্রুত? কোডটি কেবল স্ব-ব্যাখ্যামূলক।
মোটি শ্নের

1
@ মতিশ্নোর এটি দেখতে আরও জটিল দেখায়। একটি এভিএক্স নিবন্ধের আকার 32 বাইট রয়েছে। সুতরাং তিনি aনিবন্ধে কত মান মান ফিট করে তা গণনা করেন । এরপরে, তিনি সমস্ত 32 বাইট ব্লকের উপরে লুপ করেন, এটি পয়েন্টার গাণিতিক ( (float *)((a)+x)) ব্যবহার করে সম্পূর্ণরূপে ওভাররাইট করা উচিত । দুটি _mm256আন্তঃসূচী (শুরু দিয়ে ) কেবল একটি শূন্য-ইনিশিয়েলড 32 বাইট রেজিস্টার তৈরি করে এটি বর্তমান পয়েন্টারে সঞ্চয় করে। এটি প্রথম 3 লাইন। বাকিগুলি কেবলমাত্র সমস্ত বিশেষ ক্ষেত্রে পরিচালনা করে যেখানে সর্বশেষ 32 বাইবেট ব্লকটি পুরোপুরি ওভাররাইট করা উচিত নয়। এটি ভেক্টরাইজেশনের কারণে দ্রুত হয়। - আমি আশা করি এটি সাহায্য করবে.
উইচমাস্টার

11

থেকে memset():

memset(myarray, 0, sizeof(myarray));

sizeof(myarray)আকারটি myarrayসংকলন সময়ে জানা থাকলে আপনি ব্যবহার করতে পারেন । অন্যথায়, আপনি যদি গতিশীল আকারের অ্যারে ব্যবহার করে থাকেন যেমন যেমন mallocবা এর মাধ্যমে প্রাপ্ত new, আপনার দৈর্ঘ্যের উপর নজর রাখতে হবে।


2
অ্যারে আকারটি সংকলন সময়ে জানা না গেলেও আকার কাজ করবে। (অবশ্যই এটির অ্যারে কেবল তখনই)
asaelr

2
@ এসেলার: সি ++ sizeofএ সর্বদা সংকলন-সময়ে মূল্যায়ন করা হয় (এবং ভিএলএএস দিয়ে ব্যবহার করা যায় না)। সি 99 এ, ভিএলএস-এর ক্ষেত্রে এটি রানটাইম এক্সপ্রেশন হতে পারে।
বেন ভয়েগট

@ বেনভয়েট ভাল, প্রশ্ন উভয় cএবং সম্পর্কে c++। আমি অ্যালেক্সের উত্তরে মন্তব্য করেছি, তাতে বলা হয়েছে "যদি ম্যারিারের আকারটি সংকলন সময়ে জানা যায় তবে আপনি মাপের (মায়ার্য) ব্যবহার করতে পারেন"।
আসল

2
@ এসেলার: এবং সি ++ এ তিনি সম্পূর্ণ সঠিক। আপনার মন্তব্যটি C99 বা VLAs সম্পর্কে কিছু বলেনি, তাই আমি এটি স্পষ্ট করতে চেয়েছিলাম।
বেন ভয়েগট

5

আপনি ব্যবহার করতে পারেন memset, তবে কেবলমাত্র আমাদের ধরণের নির্বাচন অবিচ্ছেদ্য প্রকারের মধ্যে সীমাবদ্ধ।

সি ক্ষেত্রে সাধারণ ক্ষেত্রে এটি ম্যাক্রো বাস্তবায়নের জন্য অর্থবোধ করে

#define ZERO_ANY(T, a, n) do{\
   T *a_ = (a);\
   size_t n_ = (n);\
   for (; n_ > 0; --n_, ++a_)\
     *a_ = (T) { 0 };\
} while (0)

এটি আপনাকে সি ++ দেবে - যেমন কার্যকারিতা যা আপনাকে হ্যাকের মতো অবলম্বন না করে কোনও ধরণের অবজেক্টের অ্যারে "রিসেট টু জিরো" করতে দেয় memset। মূলত, এটি সি ++ ফাংশন টেমপ্লেটের একটি সি এনালগ, ব্যতীত আপনাকে প্রকারের যুক্তিটি স্পষ্টভাবে উল্লেখ করতে হবে।

তার উপরে আপনি ক্ষয় নষ্ট অ্যারেগুলির জন্য একটি "টেম্পলেট" তৈরি করতে পারেন

#define ARRAY_SIZE(a) (sizeof (a) / sizeof *(a))
#define ZERO_ANY_A(T, a) ZERO_ANY(T, (a), ARRAY_SIZE(a))

আপনার উদাহরণে এটি হিসাবে প্রয়োগ করা হবে

int a[100];

ZERO_ANY(int, a, 100);
// or
ZERO_ANY_A(int, a);

এটি লক্ষণীয় যে বিশেষত স্কেলারের ধরণের সামগ্রীর জন্য যে কোনও টাইপ-ইন্ডিপেন্ডেন্ট ম্যাক্রো প্রয়োগ করতে পারে

#define ZERO(a, n) do{\
   size_t i_ = 0, n_ = (n);\
   for (; i_ < n_; ++i_)\
     (a)[i_] = 0;\
} while (0)

এবং

#define ZERO_A(a) ZERO((a), ARRAY_SIZE(a))

উপরের উদাহরণটি রূপান্তর করা

 int a[100];

 ZERO(a, 100);
 // or
 ZERO_A(a);

1
আমি এর ;পরে বাদ while(0)ZERO(a,n);
পড়ব

@ 0x90: হ্যাঁ, আপনি একেবারে ঠিক বলেছেন। do{}while(0)আইডিয়ামের পুরো পয়েন্টটির জন্য ;ম্যাক্রো সংজ্ঞা নেই। সংশোধন করা হয়েছে।
এএনটি

3

স্থির ঘোষণার জন্য আমি মনে করি আপনি এটি ব্যবহার করতে পারেন:

T myarray[100] = {0};

গতিশীল ঘোষণার জন্য আমি একইভাবে পরামর্শ দিই: memset


2
প্রশ্নটি বলে: "কেবল আরম্ভের জন্য নয়"।
বেন ভয়েগট

2

zero(myarray); আপনার কেবল সি ++ তে প্রয়োজনীয়।

এটি কেবল একটি শিরোনামে যুক্ত করুন:

template<typename T, size_t SIZE> inline void zero(T(&arr)[SIZE]){
    memset(arr, 0, SIZE*sizeof(T));
}

1
এটি ভুল, এটি SIZE বাইট সাফ করবে। 'মেমসেট (আরআর, 0, সাইজ * আকারের (টি));' সঠিক হবে।
কর্নেল কিসিলেউইচিজ

নিবন্ধন করুন আমি আশা করি গত 1.5 1.5 বছরে কেউ এই ফাংশনটির অনুলিপি-পেস্ট করেছেন :(
নভিন

1
আশা করি না, আমি মন্তব্য করেছি কারণ গুগল আমাকে এখানে এনেছে :)
কর্নেল কিসিলেউইচজ

1
নোট করুন যে এই ফাংশনটি zeroউদাহরণস্বরূপও সঠিক কারণটি T=char[10]যখন arrআর্গুমেন্টটি বহুমাত্রিক অ্যারে হিসাবে করা যেতে পারে char arr[5][10]
ম্যানড্রে

1
হ্যাঁ, আমি gcc 4.7.3 এর সাথে বেশ কয়েকটি কেস পরীক্ষা করেছি tested আমি এই উত্তরের জন্য নোট করা ভাল হবে বলে মনে করি, অন্যথায় আপনার প্রতিটি অ্যারের মাত্রা গণনার জন্য টেমপ্লেট বিশেষীকরণের প্রয়োজন হবে। অন্যান্য উত্তরগুলি তেমন জেনারালাইজ করে না যেমন ARRAY_SIZEম্যাক্রো, যা বহুমাত্রিক অ্যারেতে ব্যবহার করা হলে ভুল আকার দেয়, আরও ভাল নাম হতে পারে ARRAY_DIM<n>_SIZE
ম্যান্ড্রেকে

1

আমি যে ফাংশনটি ব্যবহার করি তা এখানে:

template<typename T>
static void setValue(T arr[], size_t length, const T& val)
{
    std::fill(arr, arr + length, val);
}

template<typename T, size_t N>
static void setValue(T (&arr)[N], const T& val)
{
    std::fill(arr, arr + N, val);
}

আপনি এটিকে কল করতে পারেন:

//fixed arrays
int a[10];
setValue(a, 0);

//dynamic arrays
int *d = new int[length];
setValue(d, length, 0);

উপরে মেমসেট ব্যবহারের চেয়ে বেশি সি ++ 11 উপায় রয়েছে। এছাড়াও আপনি আকার নির্দিষ্টকরণের সাথে গতিশীল অ্যারে ব্যবহার করলে সংকলনের সময় ত্রুটি পাবেন।


আসল প্রশ্নটি সি-তে নয়, সি ++ নয়, সুতরাং স্টাড :: পূরণ একটি সঠিক উত্তর হতে পারে না
মোটি শ্নের
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.