আরআইএসসি-ভি এর জন্য আপনি সম্ভবত জিসিসি / ঝনঝন ব্যবহার করছেন।
মজাদার ঘটনা: জিসিসি এই স্বর বিট্যাক ট্রিকসটির কিছু জানে (অন্যান্য উত্তরে দেখানো হয়েছে) এবং হার্ডওয়্যার সিমডি নির্দেশাবলী ছাড়াই লক্ষ্যমাত্রার জন্য জিএনইউ সি নেটিভ ভেক্টরগুলির সাথে কোডটি সংকলন করার সময় সেগুলি আপনার জন্য ব্যবহার করতে পারে । (তবে আরআইএসসি-ভি এর জন্য ঝাঁকুনি কেবল সাদাসিধে এটিকে স্কেলার অপারেশনে তালিকাভুক্ত করবে, তাই আপনি যদি সংকলকগুলির জুড়ে ভাল পারফরম্যান্স চান তবে আপনার নিজের এটি করতে হবে)।
নেটিভ ভেক্টর সিনট্যাক্সের একটি সুবিধা হ'ল হার্ডওয়্যার সিমডি সহ কোনও মেশিনকে টার্গেট করার সময় এটি আপনার বিট্যাক বা এর মতো ভয়াবহ কিছু অটো-ভেক্টরাইজ করার পরিবর্তে এটি ব্যবহার করবে।
এটি vector -= scalar
অপারেশন লিখতে সহজ করে তোলে ; সিনট্যাক্স জাস্ট ওয়ার্কস, স্প্রেলিটি ব্রডকাস্টিং ওরফে আপনার জন্য স্কেলারকে ছড়িয়ে দিচ্ছে।
এছাড়াও লক্ষ করুন যে একটি uint64_t*
থেকে লোডটি uint8_t array[]
কঠোর-এলিয়াসিং ইউবি, সুতরাং এটির সাথে সাবধানতা অবলম্বন করুন। (এটি আরও দেখুন যে গ্লিবসি-এর স্ট্রেন দ্রুত চালানোর জন্য এত জটিল হওয়া দরকার কেন? পুনরায়: স্বর বিট্যাকসকে কঠোর-এলিয়াসিংকে খাঁটি সিতে নিরাপদ করে তোলা)। আপনি এটি ঘোষণার জন্য এর মতো কিছু চাইতে uint64_t
পারেন যা আপনি অন্য কোনও অবজেক্ট অ্যাক্সেসের জন্য পয়েন্টার-কাস্ট করতে পারেন, যেমন char*
আইএসও সি / সি ++ এ কীভাবে কাজ করে।
অন্যান্য উত্তরের সাথে ব্যবহারের জন্য uint8_t ডেটা একটি uint64_t এ পেতে এটি ব্যবহার করুন:
// GNU C: gcc/clang/ICC but not MSVC
typedef uint64_t aliasing_u64 __attribute__((may_alias)); // still requires alignment
typedef uint64_t aliasing_unaligned_u64 __attribute__((may_alias, aligned(1)));
অ্যালিজিং-সেফ লোডগুলি করার অন্যান্য উপায়টি memcpy
একটি এর সাথে রয়েছে uint64_t
যা alignof(uint64_t
অ্যালাইনমেন্টের প্রয়োজনীয়তাও সরিয়ে দেয় । কিন্তু আইএসএগুলিতে দক্ষ স্বাক্ষরযুক্ত লোড ছাড়াই, জিসিসি / ক্ল্যাং memcpy
যখন পয়েন্টারটি সারিবদ্ধ হয় তা প্রমাণ করতে না পারে তখন ইনলাইন এবং অপ্টিমাইজ করে না, যা পারফরম্যান্সের জন্য বিপর্যয়কর হবে।
টি এল: ডিআর: আপনার সেরা বাজি আপনি ডাটা ডিক্লেয়ার হয়uint64_t array[...]
বা পরিবর্তনশীল যেমন বরাদ্দ uint64_t
, বা বিশেষalignas(16) uint64_t array[];
অন্তত 8 বাইট, বা 16 নিশ্চিত প্রান্তিককরণ যদি আপনি নির্দিষ্ট করে alignas
।
যেহেতু uint8_t
প্রায় অবশ্যই unsigned char*
, এটি একটি এর বাইট অ্যাক্সেস করতে নিরাপদ uint64_t
মাধ্যমে uint8_t*
(কিন্তু ভাইস একটি uint8_t অ্যারের জন্য বিপরীতভাবে)। সুতরাং এই বিশেষ ক্ষেত্রে যেখানে সংকীর্ণ উপাদান প্রকার unsigned char
, আপনি কঠোর-এলিয়াসিং সমস্যাটিকে পাশ কাটাতে পারেন কারণ char
বিশেষ।
জিএনইউ সি নেটিভ ভেক্টর সিনট্যাক্স উদাহরণ:
গনুহ সি নেটিভ ভেক্টর সবসময় তাদের অন্তর্নিহিত ধরনের সঙ্গে ওরফে করার অনুমতি দেওয়া হয় (যেমন int __attribute__((vector_size(16)))
নিরাপদে ওরফে করতে পারেন int
কিন্তু float
বা uint8_t
বা অন্য কিছু।
#include <stdint.h>
#include <stddef.h>
// assumes array is 16-byte aligned
void dec_mem_gnu(uint8_t *array) {
typedef uint8_t v16u8 __attribute__ ((vector_size (16), may_alias));
v16u8 *vecs = (v16u8*) array;
vecs[0] -= 1;
vecs[1] -= 1; // can be done in a loop.
}
কোনও এইচডাব্লু সিমডি ছাড়াই আরআইএসসি-ভি এর জন্য, আপনি vector_size(8)
দক্ষতার সাথে ব্যবহার করতে পারেন এমন গ্রানুলারিটি প্রকাশ করতে এবং আরও ছোট ভেক্টর হিসাবে দ্বিগুণ করতে পারেন।
তবে vector_size(8)
জিসিসি এবং ঝনঝন উভয়ের সাথেই x86 এর জন্য খুব নির্বোধের সাথে সংকলন করে: জিসিসি জিপি-ইন্টিজার রেজিস্টারগুলিতে স্বওয়ার বিট্যাকগুলি ব্যবহার করে, একটি 16 বাইট এক্সএমএম রেজিস্ট্রার পূরণ করার জন্য 2 বাইট উপাদানগুলিতে ঝাঁকুনি খালি করে রাখে তারপরে পুনঃস্থাপন করে। (এমএমএক্স এতটাই অপ্রচলিত যে জিসিসি / কলং এটি ব্যবহার করে বিরক্তও করে না, কমপক্ষে x86-64 এর জন্য নয়))
তবে vector_size (16)
( গডবোল্ট ) দিয়ে আমরা প্রত্যাশিত movdqa
/ পাই paddb
। (এর দ্বারা উত্পন্ন সমস্ত-ভেক্টর সহ pcmpeqd same,same
)। সঙ্গে -march=skylake
আমরা এখনও এক YMM পরিবর্তে দুটি পৃথক XMM অপস পেতে, তাই দুর্ভাগ্যবশত বর্তমান কম্পাইলার এছাড়াও ব্যাপকতর ভেক্টর মধ্যে নয় "স্বয়ং-vectorize" ভেক্টর অপস না: /
এআরচ For৪ এর জন্য, এটি ব্যবহার করা খুব খারাপ নয় vector_size(8)
( গডবোল্ট ); এআরএম / এআরচ 64 স্থানীয়ভাবে 8 বা 16-বাইট অংশগুলিতে d
বা q
রেজিস্টারে কাজ করতে পারে।
সুতরাং আপনি সম্ভবত vector_size(16)
x86, RISC-V, আর্ম / AArch64, এবং পাওয়ার জুড়ে পোর্টেবল পারফরম্যান্স চাইলে সম্ভবত সম্ভবত সংকলন করতে চান । তবে অন্য কিছু আইএসএ এমপিএস এমএসএ-র মত like৪-বিট পূর্ণসংখ্যার নিবন্ধের মধ্যে সিমডি করে।
vector_size(8)
এএসএমটি দেখতে সহজ করে তোলে (কেবলমাত্র একটি রেজিস্টার ডেটা মূল্য): গডবোল্ট সংকলক এক্সপ্লোরার
# GCC8.2 -O3 for RISC-V for vector_size(8) and only one vector
dec_mem_gnu(unsigned char*):
lui a4,%hi(.LC1) # generate address for static constants.
ld a5,0(a0) # a5 = load from function arg
ld a3,%lo(.LC1)(a4) # a3 = 0x7F7F7F7F7F7F7F7F
lui a2,%hi(.LC0)
ld a2,%lo(.LC0)(a2) # a2 = 0x8080808080808080
# above here can be hoisted out of loops
not a4,a5 # nx = ~x
and a5,a5,a3 # x &= 0x7f... clear high bit
and a4,a4,a2 # nx = (~x) & 0x80... inverse high bit isolated
add a5,a5,a3 # x += 0x7f... (128-1)
xor a5,a4,a5 # x ^= nx restore high bit or something.
sd a5,0(a0) # store the result
ret
আমি মনে করি এটি অন্যান্য নন-লুপিং উত্তরের মতো একই প্রাথমিক ধারণা; বহন রোধ করে তারপরে ফলাফল ঠিক করা।
এটি 5 টি ALU নির্দেশিকাগুলি, আমি মনে করি শীর্ষ উত্তরগুলির চেয়ে খারাপ। তবে দেখে মনে হচ্ছে সমালোচনামূলক পথের বিলম্বটি কেবল 3 টি চক্র, যেখানে দুটি নির্দেশের দুটি চেইন প্রতিটি এক্সওআরকে নিয়ে যায়। @ রিইনস্টেট মনিকা - s - এর উত্তর 4-চক্র ডিপ চেইনের (x86 এর জন্য) সংকলন করে। 5-চক্রের লুপ থ্রুটপুটটি sub
সমালোচনামূলক পথে একটি নিষ্পাপকে অন্তর্ভুক্ত করে এবং লুপটি বিলম্বিত করতে বাধা দেয়।
তবে ঝাঁকুনির সাথে এটি বেহুদা। এমনকি এটি লোড হওয়া একই ক্রমে এটি যুক্ত এবং সঞ্চয় করে না তাই এটি ভাল সফ্টওয়্যার পাইপলাইনিংও করে না!
# RISC-V clang (trunk) -O3
dec_mem_gnu(unsigned char*):
lb a6, 7(a0)
lb a7, 6(a0)
lb t0, 5(a0)
...
addi t1, a5, -1
addi t2, a1, -1
addi t3, a2, -1
...
sb a2, 7(a0)
sb a1, 6(a0)
sb a5, 5(a0)
...
ret