অন্যরা যেমন বলেছে, এর অর্থ যদি সি ++ 14 এর কিছু না হয় , তাই আসুন আমরা __restrict__
জিসিসি এক্সটেনশনটি বিবেচনা করি যা সি 99 এর মতোই হয় restrict
।
C99
restrict
বলেছেন যে দুটি পয়েন্টার ওভারল্যাপিং মেমরি অঞ্চলে নির্দেশ করতে পারে না। সর্বাধিক সাধারণ ব্যবহার হ'ল ফাংশন আর্গুমেন্টের জন্য।
এটি কীভাবে ফাংশনটি বলা যেতে পারে তা সীমাবদ্ধ করে তবে আরও সংকলন অপ্টিমাইজেশনের জন্য অনুমতি দেয়।
যদি কলার restrict
চুক্তিটি অনুসরণ না করে তবে অপরিবর্তিত আচরণ।
C99 N1256 খসড়া 6.7.3 / 7 "প্রকার কোয়ালিফায়ার" বলেছেন:
সীমাবদ্ধ কোয়ালিফায়ার (যেমন রেজিস্টার স্টোরেজ ক্লাসের) এর উদ্দেশ্যযুক্ত ব্যবহারটি অপ্টিমাইজেশান প্রচার করা, এবং একটি মানানসই প্রোগ্রাম রচনা করে সমস্ত প্রিপ্রোসেসিং অনুবাদ ইউনিট থেকে বাছাইপর্বের সমস্ত উদাহরণ মুছে ফেলার অর্থ এর অর্থ পরিবর্তন হয় না (অর্থাত্ পর্যবেক্ষণযোগ্য আচরণ)।
এবং 7.7.৩.১ "সীমাবদ্ধতার আনুষ্ঠানিক সংজ্ঞা" গুরুর বিবরণ দেয়।
একটি সম্ভাব্য অপ্টিমাইজেশন
উইকিপিডিয়া উদাহরণ হয় খুব আলোকজ্জ্বল।
এটা তোলে পরিষ্কারভাবে দেখায় কিভাবে যেমন এটা এক সমাবেশ নির্দেশ সংরক্ষণ করার অনুমতি দেয় ।
সীমাবদ্ধ ছাড়াই:
void f(int *a, int *b, int *x) {
*a += *x;
*b += *x;
}
সিউডো সমাবেশ:
load R1 ← *x ; Load the value of x pointer
load R2 ← *a ; Load the value of a pointer
add R2 += R1 ; Perform Addition
set R2 → *a ; Update the value of a pointer
; Similarly for b, note that x is loaded twice,
; because a may be equal to x.
load R1 ← *x
load R2 ← *b
add R2 += R1
set R2 → *b
সীমাবদ্ধ সহ:
void fr(int *__restrict__ a, int *__restrict__ b, int *__restrict__ x);
সিউডো সমাবেশ:
load R1 ← *x
load R2 ← *a
add R2 += R1
set R2 → *a
; Note that x is not reloaded,
; because the compiler knows it is unchanged
; load R1 ← *x
load R2 ← *b
add R2 += R1
set R2 → *b
জিসিসি কি আসলেই তা করে?
g++
4.8 লিনাক্স x86-64:
g++ -g -std=gnu++98 -O0 -c main.cpp
objdump -S main.o
সাথে -O0
, তারা একই।
সহ -O3
:
void f(int *a, int *b, int *x) {
*a += *x;
0: 8b 02 mov (%rdx),%eax
2: 01 07 add %eax,(%rdi)
*b += *x;
4: 8b 02 mov (%rdx),%eax
6: 01 06 add %eax,(%rsi)
void fr(int *__restrict__ a, int *__restrict__ b, int *__restrict__ x) {
*a += *x;
10: 8b 02 mov (%rdx),%eax
12: 01 07 add %eax,(%rdi)
*b += *x;
14: 01 06 add %eax,(%rsi)
নিরবচ্ছিন্নদের জন্য, কলিং কনভেনশনটি হ'ল:
rdi
= প্রথম প্যারামিটার
rsi
= দ্বিতীয় প্যারামিটার
rdx
= তৃতীয় পরামিতি
জিসিসি আউটপুটটি উইকির নিবন্ধের চেয়ে আরও পরিষ্কার ছিল: 4 টি নির্দেশাবলী বনাম 3 নির্দেশাবলী।
অ্যারেগুলির
এখনও অবধি আমাদের একক নির্দেশনা সঞ্চয় রয়েছে, তবে পয়েন্টার যদি সাধারণ ব্যবহারের ক্ষেত্রে লুপগুলি সরিয়ে ফেলার জন্য উপস্থাপন করে তবে সুপার ক্যাট এবং মাইকেল দ্বারা উল্লিখিত নির্দেশাবলীর একগুচ্ছ সংরক্ষণ করা যেতে পারে ।
উদাহরণস্বরূপ বিবেচনা করুন:
void f(char *restrict p1, char *restrict p2, size_t size) {
for (size_t i = 0; i < size; i++) {
p1[i] = 4;
p2[i] = 9;
}
}
কারণ restrict
, একটি স্মার্ট কম্পাইলার (অথবা মানব), যে নিখুত পারে:
memset(p1, 4, size);
memset(p2, 9, size);
কোনটি সম্ভাব্যভাবে আরও কার্যকর যেহেতু এটি একটি শালীন libc বাস্তবায়নের (যেমন গ্লিবসি) অ্যাসেম্বলিটি অপ্টিমাইজড হতে পারে , পারফরম্যান্সের ক্ষেত্রে স্ট্যান্ড :: মেমকি () বা স্টাডি :: কপি () ব্যবহার করা কি আরও ভাল? , সম্ভবত সিমডি নির্দেশাবলী ।
সীমাবদ্ধ না করে এই অপ্টিমাইজেশানটি করা যায়নি, উদাহরণস্বরূপ বিবেচনা করুন:
char p1[4];
char *p2 = &p1[1];
f(p1, p2, 3);
তারপরে for
সংস্করণটি করে:
p1 == {4, 4, 4, 9}
যখন memset
সংস্করণ তোলে:
p1 == {4, 9, 9, 9}
জিসিসি কি আসলেই তা করে?
জিসিসি 5.2.1. লিনাক্স x86-64 উবুন্টু 15.10:
gcc -g -std=c99 -O0 -c main.c
objdump -dr main.o
সঙ্গে -O0
, উভয় একই।
সহ -O3
:
সীমাবদ্ধ সহ:
3f0: 48 85 d2 test %rdx,%rdx
3f3: 74 33 je 428 <fr+0x38>
3f5: 55 push %rbp
3f6: 53 push %rbx
3f7: 48 89 f5 mov %rsi,%rbp
3fa: be 04 00 00 00 mov $0x4,%esi
3ff: 48 89 d3 mov %rdx,%rbx
402: 48 83 ec 08 sub $0x8,%rsp
406: e8 00 00 00 00 callq 40b <fr+0x1b>
407: R_X86_64_PC32 memset-0x4
40b: 48 83 c4 08 add $0x8,%rsp
40f: 48 89 da mov %rbx,%rdx
412: 48 89 ef mov %rbp,%rdi
415: 5b pop %rbx
416: 5d pop %rbp
417: be 09 00 00 00 mov $0x9,%esi
41c: e9 00 00 00 00 jmpq 421 <fr+0x31>
41d: R_X86_64_PC32 memset-0x4
421: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
428: f3 c3 repz retq
memset
প্রত্যাশা অনুযায়ী দুটি কল।
কোনও সীমাবদ্ধ ছাড়াই: কোনও স্টিডলিব কল নেই, কেবলমাত্র 16 টি পুনরাবৃত্তির প্রশস্ত লুপটি আন্রোলিং করছে যা আমি এখানে পুনরুত্পাদন করার উদ্দেশ্যে চাই না :-)
আমি তাদের মানদণ্ডে ধৈর্য ধরিনি, তবে আমি বিশ্বাস করি যে সীমাবদ্ধ সংস্করণটি আরও দ্রুত হবে।
কঠোর আলিয়াজিংয়ের নিয়ম
restrict
মূলশব্দটি কেবল সামঞ্জস্যপূর্ণ ধরণের পয়েন্টারগুলিকেই প্রভাবিত করে (উদাহরণস্বরূপ দুটি int*
) কারণ কঠোর আলিয়াজিং বিধিগুলি বলে যে অসামঞ্জস্যিত প্রকারের aliasing পূর্বনির্ধারিতভাবে সংজ্ঞায়িত আচরণ, এবং তাই সংযোজকরা ধরে নিতে পারে যে এটি ঘটে না এবং অপ্টিমাইজ করা যায় না।
দেখুন: আলিয়াসের কঠোর নিয়ম কী?
এটি কি রেফারেন্সের জন্য কাজ করে?
জিসিসি ডক্স অনুসারে এটি করে: https://gcc.gnu.org/onlinesocs/gcc-5.1.0/gcc/ সংশ্লেষযুক্ত- পয়েন্টারস html সিনট্যাক্স সহ:
int &__restrict__ rref
এমনকি this
সদস্য ফাংশনগুলির জন্য একটি সংস্করণ রয়েছে :
void T::fn () __restrict__
restrict
একটি সি 99 কীওয়ার্ড। হ্যাঁ, আরপিবার্ট এস বার্নস, আমি জানি যে বেশিরভাগ সংকলক সমর্থন করে__restrict__
। আপনি নোট করবেন যে ডাবল আন্ডারস্কোর সহ যে কোনও কিছুই সংজ্ঞা দ্বারা, বাস্তবায়ন নির্দিষ্ট এবং এভাবে সি ++ নয় , তবে এর একটি সংকলক নির্দিষ্ট সংস্করণ।