স্কেলার কোড নির্দিষ্ট কোনো শর্তের প্রতিক্রিয়ায় ব্রাঞ্চ করতে বিভিন্ন ইন্সট্রাকশন দ্বারা সেট করা ফ্ল্যাগের উপর নির্ভর করে। প্যাকড মানগুলি কিন্তু একটি নয়, বরং সমান্তরালে অনেকগুলি মান উপস্থাপন করে। একটি একক শর্ত একটি লেনের জন্য ব্যর্থ হতে পারে এবং অন্যটির জন্য সফল হতে পারে।
এই কারণেই SIMD কোড ডিফল্টভাবে ব্রাঞ্চলেস।
ফ্ল্যাগের উপর নির্ভর করার পরিবর্তে, প্যাকড তুলনা সাধারণত ডেস্টিনেশন অপারেন্ডে একটি মাস্ক তৈরি করে।
প্রতিটি লেনের জন্য, তুলনাটি সত্য হলে পুরো লেনটিকে এক দিয়ে এবং মিথ্যা হলে শূন্য দিয়ে পূর্ণ করে।
সাইনড ইন্টিজার হিসেবে পড়লে, একটি সত্য লেন -1 এবং একটি মিথ্যা লেন 0।
এই মাস্কটি তখন বিটওয়াইজ অপারেশনগুলির সাথে কম্পোজ করা যেতে পারে নির্দিষ্ট লেন ফিল্টার করতে।
একটি স্কেলার cmp জেনেরিক অর্থে, কারণ এটি একই সময়ে বিভিন্ন ফ্ল্যাগ সেট করতে ব্যবহৃত হয়।
অন্য একটি ইন্সট্রাকশন তখন সেই ফ্ল্যাগগুলি ব্যবহার করে ব্রাঞ্চ বা গণনা করতে পারে।
কিন্তু, যেহেতু একটি প্যাকড তুলনা একইসাথে একটি শর্ত পরীক্ষা করে এবং একটি মাস্ক গণনা করে, এটি জেনেরিক নয়। তুলনাটিকে পরীক্ষা করা সঠিক শর্তটি দিতে হবে।
এটি করার দুটি উপায় আছে:
eq, এবং বৃহত্তরের জন্য gt।
অন্যান্য ভ্যারিয়েন্টগুলি এগুলির একটির ফলাফল কম্পোজ করে তৈরি করা হয়।ইন্টিজার তুলনায় একটি নির্দিষ্ট কন্ডিশনাল সাফিক্স ব্যবহার ছাড়া, সিনট্যাক্সটি আমরা ইতিমধ্যে দেখেছি একই কাঠামো অনুসরণ করে:
p + cmp + শর্ত + সাইজ (b, w, d বা q)।cmp + p + সাইজ (s বা d)।
শর্তটি একটি অতিরিক্ত অপারেন্ড হিসেবে ইমিডিয়েটে পাস করা হয়।উল্লিখিত হিসাবে, শুধুমাত্র সমতা এবং বৃহত্তরের জন্য ইন্টিজার তুলনা আছে:
| instruction | description |
|---|---|
pcmpeqb, pcmpeqw, pcmpeqd, pcmpeqq
|
per-lane equality |
pcmpgtb, pcmpgtw, pcmpgtd, pcmpgtq
|
per-lane signed greater-than |
movdqa xmm0, [rel scores]
pcmpgtd xmm0, [rel threshold] ; lane i = 0xFFFFFFFF (-1) if scores[i] > threshold[i], else 0
এর চেয়ে কম তুলনা তৈরি করতে, অপারেন্ডগুলো অদলবদল করে gt ব্যবহার করুন: a < b == b > a।
লক্ষ্য করুন যে তুলনাটি সাইনড। একটি আনসাইনড তুলনা করতে, উভয় অপারেন্ডের শীর্ষ বিটটি ফ্লিপ করুন। এটি একটি মাস্কের সাথে XOR করে করা যেতে পারে যেখানে শুধুমাত্র শীর্ষ বিটটি সেট থাকে।
দুটি কাজের ইডিয়ম হলো:
উদাহরণস্বরূপ:
pxor xmm4, xmm4 ; xmm4 = all zeros
pcmpeqd xmm7, xmm7 ; xmm7 = all ones
সব-শূন্য এবং সব-এক হলো কমন মাস্ক, যা যথাক্রমে "সর্বত্র মিথ্যা" এবং "সর্বত্র সত্য" এনকোড করতে ব্যবহৃত হয়।
এগুলি প্যাকড 0 বা প্যাকড -1 উপস্থাপন করতেও ব্যবহার করা যেতে পারে, যা কমন সেন্টিনেল ভ্যালু।
উদাহরণস্বরূপ, স্ট্রিং-এর শেষ নির্দেশকারী NUL হলো একটি 0।
ফ্লোটিং-পয়েন্ট লেনগুলি ভিন্ন আকার ব্যবহার করে: একটি ইন্সট্রাকশন, cmpps (এবং ৬৪-বিট লেনের জন্য cmppd), যেখানে শর্তটি ইমিডিয়েট হিসেবে থাকে:
movaps xmm0, [rel readings]
cmpps xmm0, [rel limits], 1 ; condition 1 is "less than": lane i = all ones if readings[i] < limits[i]
NASM-এ সিউডো-অপসও আছে যা সঠিক ইমিডিয়েটে ম্যাপ করে এবং মনে রাখা সহজ।
নিচের সবকটিতে, px-এ x হতে পারে s (৩২-বিট ফ্লোট) বা d (৬৪-বিট ফ্লোট):
| pseudo-op | immediate | comparison |
|---|---|---|
cmpeqpx |
0 | a == b |
cmpltpx |
1 | a < b |
cmplepx |
2 | a <= b |
cmpunordpx |
3 | a is NaN or b is NaN |
cmpneqpx |
4 | a != b |
cmpnltpx |
5 | a >= b |
cmpnlepx |
6 | a > b |
cmpordpx |
7 | neither a nor b is NaN |
একটি মাস্ক একটি শর্তের ফলাফল এনকোড করে। এটি তখন সেই শর্ত অনুযায়ী দুটি মানের সেটের মধ্যে, লেন দ্বারা লেন, নির্বাচন করতে ব্যবহার করা যেতে পারে। আমরা একটি মান থেকে লেনটি নিই যেখানে মাস্কটি সত্য, অন্যটি থেকে যেখানে মিথ্যা:
; result = (a AND mask) OR (b AND NOT mask)
movdqa xmm2, xmm0 ; xmm0 holds the mask, keep a copy
pand xmm2, xmm3 ; xmm2 = a AND mask: lanes of a where mask is true
pandn xmm0, xmm4 ; xmm0 = NOT mask AND b: lanes of b where mask is false
por xmm2, xmm0 ; combine the two halves
লক্ষ্য করুন যে pandn-এর অসমতা এখানে কাজে লাগে: মাস্কটি ডেস্টিনেশনে অবস্থান করে, নেগেট হয় এবং একটি একক ইন্সট্রাকশনে b থেকে নির্বাচন করে।
এই প্যাটার্নটি হলো ব্রাঞ্চলেস নির্বাচনের প্যাকড রূপ।
প্রতিটি লেন গণনা করা হয়, এবং মাস্কটি একাই সিদ্ধান্ত নেয় কোন মানটি টিকে থাকবে, কোথাও কোনো jcc ছাড়াই।
এমন ইন্সট্রাকশন আছে যারা সেই একই নির্বাচন সরাসরি সম্পাদন করে, মাস্ক রেজিস্টার থেকে প্রতি এলিমেন্টে একটি বিট পড়ে। এগুলিকে ব্লেন্ড ইন্সট্রাকশন বলা হয়:
| instruction | element | mask source |
|---|---|---|
pblendvb |
byte | implicit xmm0
|
blendvps |
32-bit lane | implicit xmm0
|
blendvpd |
64-bit lane | implicit xmm0
|
লক্ষ্য করুন যে প্রথম ইন্সট্রাকশনটি ইন্টিজার সিনট্যাক্স অনুসরণ করে, অন্যদিকে বাকি দুটি ফ্লোট সিনট্যাক্স অনুসরণ করে। কিন্তু, যেহেতু এই ইন্সট্রাকশনগুলি কেবল কাঁচা বাইট নির্বাচন করে, এগুলির যেকোনোটি ইন্টিজার এবং ফ্লোট উভয়ের সাথে ব্যবহার করা যেতে পারে।
প্রতিটি এলিমেন্টের জন্য, ব্লেন্ডটি ডেস্টিনেশন রাখে যখন মিলে যাওয়া মাস্ক এলিমেন্টের শীর্ষ বিটটি ক্লিয়ার থাকে, এবং সোর্স নেয় যখন এটি সেট থাকে।
শুধুমাত্র সেই শীর্ষ বিট বিবেচনা করা হয়, যা একটি তুলনা মাস্ক পূরণ করে, কারণ এর লেনগুলি সব এক বা সব শূন্য।
মাস্ক রেজিস্টারটি সর্বদা xmm0, যা ইমপ্লিসিট:
movaps xmm0, [rel mask] ; the selecting mask must be in xmm0
movaps xmm1, [rel b] ; destination: kept where the mask bit is clear
blendvps xmm1, [rel a] ; source: taken where the mask bit is set
অন্য যেকোনো সাইজের জন্য একটি তুলনা মাস্ক থেকে লেন নির্বাচন করতে pblendvb ব্যবহার করাও সম্ভব।
যেহেতু একটি সত্য লেনের সমস্ত বাইট সব এক, pblendvb সেগুলির সবগুলি নির্বাচন করে।
এই ইন্সট্রাকশনগুলি সমস্তই সম্পাদিত অপারেশনের (blend) পরে v যোগ করে।
এই v মানে ভ্যারিয়েবল, কারণ নির্বাচনটি স্থির নয়: এটি একটি রেজিস্টারের উপর নির্ভর করে।
v ছাড়া ভ্যারিয়েন্টও আছে, যারা ইমিডিয়েট অনুযায়ী নির্বাচন করে।
তারা একই প্যাটার্ন অনুসরণ করে, ইমিডিয়েটের বিট i সেট থাকলে লেন i নির্বাচন করে।
যদিও শক্তিশালী, SIMD কোড স্কেলার কোডের অনেক নমনীয়তার অভাব রাখে। অনেক পরিস্থিতিতে, একটি প্যাকড রেজিস্টার থেকে স্কেলার ইন্সট্রাকশনের জগতে ফিরে যাওয়া প্রয়োজন।
movmsk ইন্সট্রাকশন পরিবার দুটি জগতের মধ্যে সেতু হিসেবে কাজ করে।
এই ইন্সট্রাকশনগুলি প্রতিটি লেনের শীর্ষ বিট একটি জেনারেল-পারপাস রেজিস্টারে এক্সট্রাক্ট করে:
| instruction | gathers | result width |
|---|---|---|
pmovmskb |
top bit of each of 16 bytes | 16 bits |
movmskps |
top bit of each of 4 dwords | 4 bits |
movmskpd |
top bit of each of 2 qwords | 2 bits |
একটি তুলনার পরে ব্যবহৃত হলে, প্রতিটি সেট বিট একটি "সত্য" লেন নির্দেশ করে এবং প্রতিটি ক্লিয়ার বিট, একটি "মিথ্যা" লেন।
এই ফলাফলটি তখন স্কেলার ইন্সট্রাকশন দিয়ে যথারীতি ম্যানিপুলেট করা যেতে পারে।
উদাহরণস্বরূপ, একটি popcnt মিলের সংখ্যা গণনা করে, এবং tzcnt প্রথমটি খুঁজে বের করে।
জেনারেল-পারপাস রেজিস্টারটি ৩২-বিট বা ৬৪-বিট চওড়া হতে পারে।
test স্কেলার ইন্সট্রাকশনের একটি প্যাকড ভ্যারিয়েন্টও আছে: ptest।
এটি তার স্কেলার প্রতিরূপের অনুরূপ কারণ এটি দুটি অপারেন্ডের মধ্যে একটি AND অপারেশন সম্পাদন করে, তাদের পরিবর্তন না করে।
test-এর বিপরীতে, ptest একটি ANDN অপারেশনও সম্পাদন করে, প্রথম অপারেন্ডটি নেগেট করে।
সুতরাং, ptest-কে pand এবং pandn-এর একটি নন-ডেস্ট্রাক্টিভ সংস্করণ হিসেবে ভাবা যেতে পারে যা ফলাফল অনুযায়ী ফ্ল্যাগ সেট করে।
এই দুটি ইন্সট্রাকশনের মতোই, ptest সম্পূর্ণ SIMD রেজিস্টারকে একটি একক লেন হিসেবে বিবেচনা করে এবং তাই এটি কোনো সাইজ প্রিফিক্স নেয় না।
যদি একটি AND অপারেশনের ফলাফল 0 হয়, তাহলে ZF সেট হয়, এবং যদি ANDN অপারেশনের ফলাফল 0 হয়, তাহলে CF সেট হয়।
এর মানে হলো ptest একটি সব-এক এবং একটি সব-শূন্য মাস্ক উভয়ই পরীক্ষা করতে ব্যবহার করা যেতে পারে:
ptest ব্যবহার করলে ZF সেট হয় শুধুমাত্র যদি রেজিস্টারটি সব শূন্য হয়।
এটি 0 পরীক্ষা করতে একটি রেজিস্টারের উপর নিজের সাথে test ব্যবহার করার কমন স্কেলার ইডিয়মের অনুকরণ করে।ptest ব্যবহার করলে CF সেট হয় শুধুমাত্র যদি রেজিস্টারটি সব এক হয়।
এছাড়াও, এটি ZF সেট করে শুধুমাত্র যদি রেজিস্টারটি সব শূন্য হয়, যা একসাথে উভয় মাস্ক পরীক্ষা করা সম্ভব করে।pxor xmm0, xmm0 ; all zeros
pcmpeqb xmm1, xmm1 ; all ones
pcmpeqb xmm2, xmm2
ptest xmm0, xmm0 ; ZF set: a register against itself detects all zeros
ptest xmm0, xmm1 ; ZF set, CF clear: xmm0 is all zeros, not all ones
ptest xmm2, xmm1 ; CF is set only if xmm2 is all ones
একটি ptest-এর ফলাফল ব্রাঞ্চ করতে বা setcc বা cmovcc-এর মতো ব্রাঞ্চলেস ইন্সট্রাকশনে যথারীতি ব্যবহার করা যেতে পারে।
আপনি একটি স্কুলের মার্কিং স্টেশন চালান, একবারে এক ব্লক করে ক্লাসের ফলাফল স্কোর করেন।
প্রতিটি ব্লকে ৪টি ফলাফল থাকে, আর স্টেশনটি ব্লকের প্রতিটি ফলাফলে একই অপারেশন প্রয়োগ করে। একটি স্কোর হলো একটি ৩২-বিট ফ্লোটিং-পয়েন্ট সংখ্যা। কয়েকটি ধাপ একটি মাস্ক নিয়ে কাজ করে: ৪টি লেনের একটি ব্লক, যেখানে প্রতিটি লেন হয় সব এক (ওই ফলাফলের জন্য একটি হ্যাঁ) নয়তো সব শূন্য (একটি না)।
আপনার পাঁচটি কাজ আছে। আপনি অপারেন্ডগুলো মেমরি অ্যাড্রেসের মাধ্যমে পান। কিছু কাজ তাদের উত্তর একটি ফলাফল অ্যাড্রেসে লেখে, আবার কিছু কাজ তা সরাসরি রিটার্ন করে।
এই অনুশীলনীর সব মেমরি অ্যাড্রেস ১৬-বাইট অ্যালাইনড।
এই অনুশীলনীর গণনাগুলো SIMD নির্দেশনা ব্যবহার করে করা উচিত।
প্রথম ধাপে প্রতিটি ফলাফল একটি থ্রেশহোল্ডের বিপরীতে গ্রেড করা হয়। একটি ফলাফল বারটি পেরোয় যখন তার স্কোর থ্রেশহোল্ডের চেয়ে কঠোরভাবে বড় হয়। থ্রেশহোল্ডের চেয়ে কম বা সমান যেকোনো স্কোর তা পেরোয় না।
flag_above_threshold ফাংশনটি ইমপ্লিমেন্ট করুন, যা তার থ্রেশহোল্ডের উপরে থাকা প্রতিটি স্কোরের জন্য একটি সব-এক লেন এবং বাকিগুলোর জন্য একটি সব-শূন্য লেন দিয়ে একটি মাস্ক তৈরি করে।
এই ফাংশনটি এই ক্রমে আর্গুমেন্ট হিসেবে নেয়:
result: একটি বাফারের মেমরি অ্যাড্রেস, যেখানে ৪টি মাস্ক লেন লেখা হয়।scores: স্কোরগুলোর মেমরি অ্যাড্রেস, যেখানে ৪টি ৩২-বিট নরমাল ফ্লোটিং-পয়েন্ট সংখ্যা থাকে (কখনোই NaN নয়)।thresholds: প্রতিটি লেনের থ্রেশহোল্ডের মেমরি অ্যাড্রেস, যেখানে ৪টি ৩২-বিট নরমাল ফ্লোটিং-পয়েন্ট সংখ্যা থাকে (কখনোই NaN নয়)।scores = {72.0, 55.0, 90.0, 40.0}
thresholds = {60.0, 60.0, 60.0, 60.0}
result = {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}
এই ফাংশনের কোনো রিটার্ন ভ্যালু নেই।
একটি আলাদা রিপোর্ট নিখুঁত ফলাফলগুলো তুলে ধরে, অর্থাৎ যেগুলো সর্বোচ্চ সম্ভাব্য নম্বরে পৌঁছেছে।
flag_perfect ফাংশনটি ইমপ্লিমেন্ট করুন, যা তার সর্বোচ্চ মানের সমান প্রতিটি স্কোরের জন্য একটি সব-এক লেন এবং বাকিগুলোর জন্য একটি সব-শূন্য লেন দিয়ে একটি মাস্ক তৈরি করে।
এই ফাংশনটি এই ক্রমে আর্গুমেন্ট হিসেবে নেয়:
result: একটি বাফারের মেমরি অ্যাড্রেস, যেখানে ৪টি মাস্ক লেন লেখা হয়।scores: স্কোরগুলোর মেমরি অ্যাড্রেস, যেখানে ৪টি ৩২-বিট নরমাল ফ্লোটিং-পয়েন্ট সংখ্যা থাকে (কখনোই NaN নয়)।maxima: প্রতিটি লেনের সর্বোচ্চ নম্বরের মেমরি অ্যাড্রেস, যেখানে ৪টি ৩২-বিট নরমাল ফ্লোটিং-পয়েন্ট সংখ্যা থাকে (কখনোই NaN নয়)।scores = {100.0, 88.0, 100.0, 73.0}
maxima = {100.0, 100.0, 100.0, 100.0}
result = {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}
এই ফাংশনের কোনো রিটার্ন ভ্যালু নেই।
প্রতিটি স্কোর ১ থেকে ৩ পর্যন্ত একটি র্যাঙ্ক পায়:
50.0-এ বা তার নিচে থাকা স্কোরের জন্য র্যাঙ্ক ১।assign_ranks ফাংশনটি ইমপ্লিমেন্ট করুন, যা প্রতিটি স্কোরের র্যাঙ্ক লেখে।
আপনার পাসিং থ্রেশহোল্ড এবং র্যাঙ্ক মানগুলো মেমরিতে প্যাকড কনস্ট্যান্ট হিসেবে ডিফাইন করা উচিত। আগের দুই কাজের ফাংশন দুটি আবার ব্যবহার করা যেতে পারে: একটি স্কোর থ্রেশহোল্ডের উপরে থাকলে সেটি অন্তত র্যাঙ্ক ২, আর সর্বোচ্চের সমান হলে র্যাঙ্ক ৩।
এই ফাংশনটি এই ক্রমে আর্গুমেন্ট হিসেবে নেয়:
result: একটি বাফারের মেমরি অ্যাড্রেস, যেখানে ৪টি র্যাঙ্ক লেখা হয়, প্রতিটি একটি ৩২-বিট আনসাইনড ইন্টিজার।scores: স্কোরগুলোর মেমরি অ্যাড্রেস, যেখানে ৪টি ৩২-বিট নরমাল ফ্লোটিং-পয়েন্ট সংখ্যা থাকে (কখনোই NaN নয়)।maxima: প্রতিটি লেনের সর্বোচ্চ নম্বরের মেমরি অ্যাড্রেস, যেখানে ৪টি ৩২-বিট নরমাল ফ্লোটিং-পয়েন্ট সংখ্যা থাকে (কখনোই NaN নয়)।scores = {40.0, 75.0, 100.0, 60.0}
maxima = {100.0, 100.0, 100.0, 100.0}
result = {1, 2, 3, 2}
এই ফাংশনের কোনো রিটার্ন ভ্যালু নেই।
বছর ধরে প্রতিটি শিক্ষার্থী একটি মোট র্যাঙ্ক গড়ে তোলে। পুরো কোহোর্টের মধ্যে কতগুলো র্যাঙ্ক একটি পাসিং থ্রেশহোল্ডের নিচে পড়ে তা স্টেশনটি গণনা করে, যাতে কতগুলো অতিরিক্ত ক্লাস নিতে হবে তা পরিকল্পনা করা যায়।
count_failures ফাংশনটি ইমপ্লিমেন্ট করুন, যা রিটার্ন করে যে প্রতিটি ব্লক মিলিয়ে কতগুলো র্যাঙ্ক পাসিং থ্রেশহোল্ডের কঠোরভাবে নিচে পড়ে।
থ্রেশহোল্ডটি ৪টি অভিন্ন লেনের একটি ব্লক হিসেবে দেওয়া হয়, তাই আপনি এটি একবার লোড করে প্রতিটি ব্লকে আবার ব্যবহার করতে পারেন।
এই ফাংশনটি এই ক্রমে আর্গুমেন্ট হিসেবে নেয়:
ranks: র্যাঙ্কগুলোর মেমরি অ্যাড্রেস, ৪-লেন ব্লকের একটি পূর্ণ সংখ্যা, প্রতিটি র্যাঙ্ক একটি ৩২-বিট আনসাইনড ইন্টিজার।block_count: ৪-লেন ব্লকের সংখ্যা, যা সর্বদা 0-এর চেয়ে বড়।pass_threshold: পাসিং থ্রেশহোল্ডের মেমরি অ্যাড্রেস, যেখানে ৪টি অভিন্ন ৩২-বিট ইন্টিজার থাকে।ranks = {1, 2, 3, 1, 2, 2, 1, 3} // 2 blocks
block_count = 2
pass_threshold = {2, 2, 2, 2}
// => 3
এই ফাংশনটি গণনাটি একটি সাইনড ৩২-বিট ইন্টিজার হিসেবে রিটার্ন করে।
রেকর্ডগুলো ফাইল করার আগে স্টেশনটি যাচাই করে কোহোর্টটি পরিষ্কার কি না: কোনো ব্লকে যদি একটিও ফলাফল ফেল না করে, তবেই সেটি পাস করে।
all_passed ফাংশনটি ইমপ্লিমেন্ট করুন, যা সব শিক্ষার্থী পাস করলে 1 এবং অন্যথায় 0 রিটার্ন করে।
একজন শিক্ষার্থী পাস করে যখন failing অ্যারের তার সংশ্লিষ্ট লেনটি সব শূন্য হয়।
এই ফাংশনটি এই ক্রমে আর্গুমেন্ট হিসেবে নেয়:
failing: ফেলিং মাস্কগুলোর মেমরি অ্যাড্রেস, ৪-লেন ব্লকের একটি পূর্ণ সংখ্যা, প্রতিটি লেন সব এক অথবা সব শূন্য।block_count: ৪-লেন ব্লকের সংখ্যা, যা সর্বদা 0-এর চেয়ে বড়।failing = {0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000} // 2 blocks
block_count = 2
// => 1
এই ফাংশনটি উত্তরটি একটি সাইনড ৩২-বিট ইন্টিজার হিসেবে রিটার্ন করে, হয় 1 অথবা 0।
Exercism-এ সাইন আপ করুন, x86-64 Assembly ট্র্যাকের 22টি কনসেপ্ট130টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।