একটি 64-বিট পূর্ণসংখ্যায় 8-বিট সংখ্যার সমান্তরাল 1 দ্বারা সমাহার, হার্ডওয়্যার সিমডি ছাড়াই SWਵਾਰ বিয়োগ


77

যদি আমার কাছে 64৪-বিট পূর্ণসংখ্যা থাকে যা আমি 8 টি উপাদান সহ প্যাকযুক্ত 8-বিট পূর্ণসংখ্যার অ্যারে হিসাবে ব্যাখ্যা করছি। 1অন্য উপাদানগুলির ফলাফলকে প্রভাবিত করে এমন কোনও উপাদান ছাড়াই ওভারফ্লো পরিচালনা করার সময় আমাকে প্রতিটি প্যাকড পূর্ণসংখ্যার থেকে ধ্রুবকটি বিয়োগ করতে হবে ।

এই মুহুর্তে আমার কাছে এই কোডটি রয়েছে এবং এটি কাজ করে তবে আমার এমন একটি সমাধান দরকার যা প্রতিটি প্যাকড 8-বিট পূর্ণসংখ্যাকে সমান্তরালে বিয়োগ করে এবং মেমরির প্রবেশাধিকার করে না। X86-এ আমি সিমডি নির্দেশাবলী ব্যবহার করতে পারি যেমন সাবট্রাক্টগুলিতে psubbসমান্তরালভাবে 8-বিট পূর্ণসংখ্যার প্যাক করা হয়েছে তবে আমি যে প্ল্যাটফর্মটির জন্য কোড করছি সেটি সিমডি নির্দেশাবলী সমর্থন করে না। (এক্ষেত্রে আরআইএসসি-ভি)।

সুতরাং আমি এর বাইটগুলির মধ্যে বহনকারী প্রচার প্রচারের জন্য ম্যানুয়ালি বাতিল করতে স্বার (একটি রেজিস্টারের মধ্যে সিমডি) করার চেষ্টা করছি uint64_t: এর সমতুল্য কিছু করছে:

uint64_t sub(uint64_t arg) {
    uint8_t* packed = (uint8_t*) &arg;

    for (size_t i = 0; i < sizeof(uint64_t); ++i) {
        packed[i] -= 1;
    }

    return arg;
}

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


5
তাদের কি 8-বিট হওয়া দরকার বা তারা পরিবর্তে 7-বিট হতে পারে?
tadman

তাদের 8-বিট দুঃখিত হতে হবে :(
ক্যাম-হোয়াইট



1
আপনি কি প্রত্যাশা করেন যে বাইটে 0xff থেকে মোড়কে শূন্য থাকে?
আলনিটাক

উত্তর:


75

আপনার যদি দক্ষ সিমডি নির্দেশাবলী সহ সিপিইউ থাকে, এসএসই / এমএমএক্স paddb( _mm_add_epi8) এছাড়াও কার্যকর able পিটার কর্ডসের জবাব জিএনইউ সি (জিসিসি / ক্ল্যাং) ভেক্টর সিনট্যাক্স এবং কঠোর-এলিয়াসিং ইউবির সুরক্ষারও বর্ণনা করে। আমি সেই উত্তরটি পর্যালোচনা করার জন্যও উত্সাহিত করি।

এটি দিয়ে uint64_tনিজেই করা পুরোপুরি পোর্টেবল, তবে এর uint8_tসাথে অ্যারে অ্যাক্সেস করার সময় প্রান্তিককরণ সমস্যা এবং কঠোর-আলিয়াজিং ইউবি এড়াতে যত্নের প্রয়োজন uint64_t*। আপনি uint64_tইতিমধ্যে আপনার ডেটা দিয়ে শুরু করে এই অংশটি প্রশ্নের বাইরে রেখে গেছেন , তবে জিএনইউ সি-র জন্য কোনও may_aliasটাইপেইফ সমস্যা সমাধান করে (তার জন্য পিটারের উত্তর দেখুন বা দেখুন memcpy)।

অন্যথায় আপনি নিজের ডেটা হিসাবে বরাদ্দ / ঘোষনা করতে uint64_tএবং uint8_t*যখন আপনি পৃথক বাইট চান তখন এর মাধ্যমে অ্যাক্সেস করতে পারেন। unsigned char*অন্য কোনও উপকরণের অনুমতি দেওয়া হয় যাতে 8-বিট উপাদানগুলির নির্দিষ্ট ক্ষেত্রে সমস্যাটি পাশ কাটে। (যদি uint8_tকিছুটা বিদ্যমান থাকে তবে সম্ভবত এটি নিরাপদ বলে ধরে নেওয়া নিরাপদ unsigned char))


দ্রষ্টব্য যে এটি পূর্বের ভুল অ্যালগরিদম থেকে পরিবর্তন (পুনর্বিবেচনার ইতিহাস দেখুন)।

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

আমরা এখানে প্রদত্ত বিয়োগ কৌশলটি সামান্য অনুকূল করতে চলেছি । তারা সংজ্ঞায়িত:

SWAR sub z = x - y
    z = ((x | H) - (y &~H)) ^ ((x ^~y) & H)

সঙ্গে Hহিসাবে সংজ্ঞায়িত করা 0x8080808080808080U(অর্থাত প্রতিটি বস্তাবন্দী পূর্ণসংখ্যা এর MSBs)। একটি হ্রাস জন্য, yহয় 0x0101010101010101U

আমরা জানি যে yএর সমস্ত এমএসবি পরিষ্কার রয়েছে, তাই আমরা কোনও একটি মুখোশ পদক্ষেপ এড়িয়ে যেতে পারি (যেমন আমাদের ক্ষেত্রে y & ~Hএকই y) গণনাটি নিম্নরূপ:

  1. আমরা x1 এর প্রতিটি উপাদানগুলির এমএসবি সেট করেছিলাম , যাতে কোনও orrowণ পরবর্তী উপাদানটিতে এমএসবির অতীত প্রচার করতে না পারে। এটিকে সমন্বিত ইনপুট বলুন Call
  2. আমরা 0x01010101010101সংশোধিত ইনপুট থেকে বিয়োগ করে প্রতিটি উপাদান থেকে 1 টি বিয়োগ করি । এটি আন্তঃ-উপাদানটি ধাপ 1 এর জন্য orrowণ গ্রহণের কারণ হয় না this এটিকে অ্যাডজাস্ট করা আউটপুট কল করুন।
  3. আমাদের এখন ফলাফলের এমএসবি সংশোধন করা দরকার। ফলাফল ঠিক করা শেষ করতে আমরা মূল ইনপুটটির বিপরীত এমএসবিগুলির সাথে সমন্বয়িত আউটপুটটি জোর করি।

অপারেশন হিসাবে লেখা যেতে পারে:

#define U64MASK 0x0101010101010101U
#define MSBON 0x8080808080808080U
uint64_t decEach(uint64_t i){
      return ((i | MSBON) - U64MASK) ^ ((i ^ MSBON) & MSBON);
}

সাধারণত, এটি সংকলক দ্বারা ইনলাইন করা হয়েছে ( এটি জোর করার জন্য সংকলক নির্দেশিকা ব্যবহার করুন ), বা অন্য ফাংশনের অংশ হিসাবে এক্সপ্রেশনটি ইনলাইনটিতে লিখিত হয়েছে।

Testcases:

in:  0000000000000000
out: ffffffffffffffff

in:  f200000015000013
out: f1ffffff14ffff12

in:  0000000000000100
out: ffffffffffff00ff

in:  808080807f7f7f7f
out: 7f7f7f7f7e7e7e7e

in:  0101010101010101
out: 0000000000000000

পারফরম্যান্সের বিশদ

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

uint64t[rax] decEach(rcx):
    movabs  rcx, -9187201950435737472
    mov     rdx, rdi
    or      rdx, rcx
    movabs  rax, -72340172838076673
    add     rax, rdx
    and     rdi, rcx
    xor     rdi, rcx
    xor     rax, rdi
    ret

নিম্নলিখিত স্নিপেটের কয়েকটি আইএসিএ পরীক্ষার সাথে:

// Repeat the SWAR dec in a loop as a microbenchmark
uint64_t perftest(uint64_t dummyArg){
    uint64_t dummyCounter = 0;
    uint64_t i = 0x74656a6d27080100U; // another dummy value.
    while(i ^ dummyArg) {
        IACA_START
        uint64_t naive = i - U64MASK;
        i = naive + ((i ^ naive ^ U64MASK) & U64MASK);
        dummyCounter++;
    }
    IACA_END
    return dummyCounter;
}

আমরা স্কাইলেক মেশিনে দেখাতে পারি যে হ্রাস, জোর, এবং তুলনা + জাম্পটি পুনরাবৃত্তি প্রতি 5 টি চক্রের নিচে সঞ্চালিত হতে পারে:

Throughput Analysis Report
--------------------------
Block Throughput: 4.96 Cycles       Throughput Bottleneck: Backend
Loop Count:  26
Port Binding In Cycles Per Iteration:
--------------------------------------------------------------------------------------------------
|  Port  |   0   -  DV   |   1   |   2   -  D    |   3   -  D    |   4   |   5   |   6   |   7   |
--------------------------------------------------------------------------------------------------
| Cycles |  1.5     0.0  |  1.5  |  0.0     0.0  |  0.0     0.0  |  0.0  |  1.5  |  1.5  |  0.0  |
--------------------------------------------------------------------------------------------------

(অবশ্যই, x86-64 এ আপনি কেবল movqএক্সএমএম রেগের জন্য লোড করতে বা প্রবেশ করতে চাইছেন paddb, সুতরাং এটি আরআইএসসি-ভি এর মতো আইএসএর জন্য কীভাবে সংকলন করে তা আরও আকর্ষণীয় হতে পারে))


4
আরআইএসসি-ভি মেশিনে চালনার জন্য আমার কোডটি দরকার যার সিমডি নির্দেশনা নেই (এখনও) এমএমএক্স
ক্যাম-হোয়াইট

2
@ ক্যাম-হোয়াইট বুঝতে পেরেছেন - এটির পরে আপনি যা করতে পারেন এটি সম্ভবত সেরা। আমি গডবোল্টের সাথে স্যানিটি করার চেষ্টা করব আরআইএসসি-র জন্য অ্যাসেম্বলিও যাচাই করব। সম্পাদনা করুন: গডবোল্টে আরআইএসসি-ভি সমর্থন নেই :(
ন্যানোফারাড

7
এখন পর্যন্ত, আসলে godbolt উপর আরআইএসসি-ভি সমর্থন মত উদাহরণস্বরূপ এই : (যে কম্পাইলার মাস্ক তৈরি মাত্রাতিরিক্ত সৃজনশীল পায় বলে মনে হয় .. ই)
হ্যারল্ড

4
এই প্যারিটি ("বহনকারী ভেক্টর" নামে পরিচিত) কৌশলটি বিভিন্ন পরিস্থিতিতে কীভাবে ব্যবহার করা যেতে পারে সে সম্পর্কে আরও পড়ুন: এমুলেটরগুলি
ডকস

4
আমি অন্য সম্পাদনা করেছি; গনুহ সি নেটিভ ভেক্টর আসলে এড়াতে কঠোর-অ্যালায়েসিং সমস্যার; একটি ভেক্টর অফ-এর উপাত্ত উপাত্ত uint8_tঅনুমোদিত is uint8_tআপনার ফাংশনটির কলকারীরা (এটিতে একটিতে uint8_tডেটা নেওয়া দরকার uint64_t) সেইগুলি যা কঠোর-এলিয়াসিং সম্পর্কে চিন্তা করতে হবে! সুতরাং সম্ভবত ওপি-তে কেবল অ্যারেগুলি ঘোষিত / বরাদ্দ করা উচিত uint64_tকারণ char*আইএসও সি ++ এ যে কোনও কিছুর নাম রাখার অনুমতি রয়েছে তবে বিপরীতে নয়।
পিটার কর্ডেস

16

আরআইএসসি-ভি এর জন্য আপনি সম্ভবত জিসিসি / ঝনঝন ব্যবহার করছেন।

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

নেটিভ ভেক্টর সিনট্যাক্সের একটি সুবিধা হ'ল হার্ডওয়্যার সিমডি সহ কোনও মেশিনকে টার্গেট করার সময় এটি আপনার বিট্যাক বা এর মতো ভয়াবহ কিছু অটো-ভেক্টরাইজ করার পরিবর্তে এটি ব্যবহার করবে।

এটি 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

13

আমি উল্লেখ করতে পারি যে আপনি একবার লিখেছেন কোডটি একবারে আপনি যদি কোনও একক uint64_t এর চেয়ে বেশি ব্যবহার শুরু করেন তবে প্রকৃতপক্ষে ভেক্টরাইজ হবে।

https://godbolt.org/z/J9DRzd


1
আপনি এখানে কি ঘটছে সে সম্পর্কে কোনও ব্যাখ্যা বা ব্যাখ্যা দিতে পারেন? এটি বেশ আকর্ষণীয় বলে মনে হচ্ছে।
n314159

2
আমি সিমডের নির্দেশনা ছাড়াই এটি করার চেষ্টা করছিলাম তবে আমি এই আকর্ষণীয়টি কম কমই পেয়েছি :)
ক্যাম-হোয়াইট

8
অন্যদিকে, সিমড কোডটি ভয়াবহ। সংকলকটি এখানে কী ঘটছে তা পুরোপুরি ভুল বুঝেছিল। ই: এটি "উদাহরণস্বরূপ এটি একটি সংকলক দ্বারা সম্পন্ন হয়েছিল কারণ কোনও মানুষ এই বোকা হবে না"
হেরোলেড

1
@ পিটারকর্ডস: আমি এমন একটি __vector_loop(index, start, past, pad)নির্মাণের ধরণটি নিয়ে আরও ভাবছিলাম যা বাস্তবায়ন হিসাবে বিবেচনা করতে পারে for(index=start; index<past; index++)[অর্থাত্ কোনও প্রয়োগ এটি কোডটি ব্যবহার করে কেবল ম্যাক্রো সংজ্ঞায়িত করতে পারে], তবে এতে কিছু প্রক্রিয়া করার জন্য একটি সংকলককে আমন্ত্রণ জানাতে লিজার শব্দার্থবিজ্ঞান থাকতে পারে কোনও পাওয়ার-অফ-টু টু মাপ অবধি pad, প্রারম্ভটি নীচের দিকে প্রসারিত করে এবং যদি তারা ইতিমধ্যে খণ্ড আকারের গুণক না হয় তবে উপরের দিকে শেষ হয়। প্রতিটি অংশের মধ্যে পার্শ্ব-প্রতিক্রিয়াগুলি অমীমাংসিত হবে, এবং যদি breakলুপের মধ্যে দেখা দেয়, অন্য রেপস ...
সুপারক্যাট

1
@ পিটারকর্ডস: যদিও restrictসহায়ক (এবং আরও সহায়ক হবে যদি স্ট্যান্ডার্ড "কমপক্ষে সম্ভাব্য উপর ভিত্তি করে" একটি ধারণাটি স্বীকৃতি দেয় এবং তারপরে নির্বোধ এবং অকার্যকর কোণার ক্ষেত্রে সোজাভাবে "ভিত্তিক" এবং "কমপক্ষে সম্ভাব্য ভিত্তিক" সংজ্ঞায়িত হন) আমার প্রস্তাবটি একটি সংকলককে অনুরোধের চেয়ে লুপের আরও সম্পাদন করার অনুমতিও দেবে - এমন কিছু যা ভেক্টরাইজেশনকে সহজতর করবে, কিন্তু যার জন্য স্ট্যান্ডার্ড কোনও বিধান করে না।
সুপারক্যাট

11

আপনি নিশ্চিত করতে পারেন যে বিয়োগটি বেশি পরিমাণে প্রবাহিত হবে না এবং তারপরে উচ্চ বিটটি ঠিক করুন:

uint64_t sub(uint64_t arg) {
    uint64_t x1 = arg | 0x80808080808080;
    uint64_t x2 = ~arg & 0x80808080808080;
    // or uint64_t x2 = arg ^ x1; to save one instruction if you don't have an andnot instruction
    return (x1 - 0x101010101010101) ^ x2;
}

আমি মনে করি এটি বাইটের সমস্ত 256 সম্ভাব্য মানগুলির জন্য কাজ করে; 0x0, 0x7f, 0x80, এবং 0xff (সংখ্যার মাঝখানে স্থানান্তরিত) যেমন বিভিন্ন ইনপুটগুলির জন্য ধ্রুবক-প্রচারের ফলাফলগুলি দেখতে আমি গডবোল্টে (আরআইএসসি-ভি ঝাঁকুনির সাথে ) গডবোল্ট.আরজেজ / ডিজিএল 9aq এ রেখেছি। ভাল লাগছে। আমি মনে করি শীর্ষ উত্তরগুলি একই জিনিসটিতে ফোটে তবে এটি আরও জটিল উপায়ে ব্যাখ্যা করে explains
পিটার কর্ডেস

সংকলকগণ এখানে নিবন্ধগুলিতে কনস্ট্যান্ট গঠনের জন্য আরও ভাল কাজ করতে পারেন। ঝনঝন নির্মাণের নির্দেশাবলী প্রচুর পরিমাণে ব্যয় করে splat(0x01)এবং splat(0x80)পরিবর্তে অন্যটির কাছ থেকে শিফ্ট পেয়ে। এমনকি উত্সে সেভাবে এটি লিখতে Godbolt.org/z/6y9v-u আরও ভাল কোড তৈরির ক্ষেত্রে সংকলকটিকে হাত ধরে না; এটি কেবল ধ্রুবক প্রচার করে।
পিটার কর্ডেস

আমি অবাক হই কেন এটি কেবল স্মৃতি থেকে ধ্রুবকটি লোড করে না; আলফা (একই ধরণের আর্কিটেকচার) এর জন্য সংকলকরা এটি করে।
ফাল হ্যাফনার

আরআইএসসি-ভি এর জন্য জিসিসি মেমরি থেকে লোড ধ্রুবকগুলি করে । দেখে মনে হচ্ছে ঝনঝন কিছু টিউনিং প্রয়োজন, যদি না ডেটা-ক্যাশে মিস করা হয় এবং নির্দেশনা থ্রুপুটটির তুলনায় ব্যয়বহুল হয়। (আলফার পরে সেই ভারসাম্যটি অবশ্যই পরিবর্তিত হতে পারে, এবং সম্ভবত আরআইএসসি-ভি এর বিভিন্ন বাস্তবায়ন পৃথক। । 20 + + 12 = 32 তাৎক্ষণিক তথ্য বিট জন্য AArch64 এর বিট-প্যাটার্ন immediates এমনকি এবং / অথবা / XOR যাও, স্মার্ট ডিকোড বনাম ঘনত্ব পছন্দ) জন্য immediates এই ব্যবহার করতে পারে
পিটার Cordes

আরআইএসসি-ভি এর জন্য জিসিসির নেটিভ-ভেক্টর স্বর দেখানো একটি উত্তর যুক্ত করেছে
পিটার

7

আপনি যা চান তা এটি নিশ্চিত না তবে এটি একে অপরের সমান্তরালে 8 টি বিয়োগগুলি করে:

#include <cstdint>

constexpr uint64_t mask = 0x0101010101010101;

uint64_t sub(uint64_t arg) {
    uint64_t mask_cp = mask;
    for(auto i = 0; i < 8 && mask_cp; ++i) {
        uint64_t new_mask = (arg & mask_cp) ^ mask_cp;
        arg = arg ^ mask_cp;
        mask_cp = new_mask << 1;
    }
    return arg;
}

ব্যাখ্যা: বিটমাস্ক 8-বিট সংখ্যার প্রত্যেকটিতে 1 দিয়ে শুরু হয়। আমরা আমাদের যুক্তি দিয়ে এটি xor। এই জায়গায় যদি আমাদের 1 থাকে তবে আমরা 1 টি বিয়োগ করেছি এবং থামতে হবে। এটি new_mask এ সম্পর্কিত বিট 0 তে সেট করে করা হয়। আমাদের যদি 0 থাকে, আমরা এটি 1 এ সেট করেছিলাম এবং বহন করতে হবে, তাই বিটটি 1 টি স্থির থাকে এবং আমরা মুখোশটি বামে স্থানান্তর করি। নতুন মুখোশের প্রজন্ম যেমন ইচ্ছা অনুযায়ী কাজ করে তবে আপনি নিজের জন্য আরও ভাল করে পরীক্ষা করে দেখুন, আমার মনে হয়, তবে দ্বিতীয় মতটি খারাপ হবে না।

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


forসমান্তরালে চলবে না, আপনি কি বিভ্রান্ত for_each?
এলটিপিসিগো

3
@ এলটিপিসিজিও না, লুপের জন্য এটি সমান্তরাল করা আমার উদ্দেশ্য নয়, এটি আসলে অ্যালগরিদমকে ভেঙে দেবে। তবে এই কোডটি সমান্তরালভাবে bit৪ বিট পূর্ণসংখ্যার বিভিন্ন 8 বিট পূর্ণসংখ্যায় কাজ করে, অর্থাৎ সমস্ত 8 বিয়োগ একই সাথে করা হয় তবে তাদের 8 টি ধাপ পর্যন্ত প্রয়োজন।
n314159

আমি বুঝতে পেরেছিলাম যে আমি যা বলছিলাম তা কিছুটা অযৌক্তিক হতে পারে তবে এটির জন্য আমার যা ধন্যবাদ দরকার ছিল তার কাছাকাছি ছিল :)
ক্যাম-হোয়াইট

4
int subtractone(int x) 
{
    int f = 1; 

    // Flip all the set bits until we find a 1 at position y
    while (!(x & f)) { 
        x = x^f; 
        f <<= 1; 
    } 

    return x^f; // return answer but remember to flip the 1 at y
} 

আপনি উপরেরটি ব্যবহার করে বিটওয়াইজ অপারেশনগুলি সহ এটি করতে পারেন এবং এই ফাংশনে 8 বার প্রেরণ করতে আপনাকে আপনার পূর্ণসংখ্যাকে 8 বিট টুকরো বিভক্ত করতে হবে। নিম্নলিখিত অংশটি কীভাবে একটি 64-বিট সংখ্যাকে আট 8-বিট মানগুলিতে বিভক্ত করবেন তা থেকে নেওয়া হয়েছিল ? আমার উপরের ফাংশন যোগ করার সাথে

uint64_t v= _64bitVariable;
uint8_t i=0,parts[8]={0};
do parts[i++] = subtractone(v&0xFF); while (v>>=8);

কেউ কীভাবে এটি জুড়ে আসে তা নির্বিশেষে এটি বৈধ সি বা সি ++


5
এটি যদিও কাজের সমান্তরালতা নয়, এটি ওপির প্রশ্ন।
নিকেল্পপ্রো

হ্যাঁ @ ননকেল্প্রো ঠিক আছে, এটি একের পর এক প্রতিটি বিয়োগফল করবে, আমি একই সাথে সমস্ত 8-বিট পূর্ণসংখ্যাকে বিয়োগ করতে চাই। আমি ধন্যবাদ ধন্যবাদ ভাই ধন্যবাদ
ক্যাম-সাদা

2
@nickelpro যখন আমি উত্তরটি শুরু করি তখন সম্পাদনাটি করা হয়নি যা প্রশ্নের সমান্তরাল অংশ বলেছিল এবং তাই আমি জমা দেওয়ার পরে অবধি লক্ষ্য করিনি, এটি অন্যদের পক্ষে কার্যকর হবে কারণ এটি কমপক্ষে উত্তরটির উত্তর দেয় অংশটি বিটওয়াইজ অপারেশন করার জন্য এবং এটি সমান্তরালভাবে কাজ for_each(std::execution::par_unseq,...করার জন্য
কিছুক্ষেত্রের

2
এটা আমার খারাপ, আমি প্রশ্নটি জমা দিয়েছিলাম তখন বুঝতে পেরেছিলাম যে এতটা সম্পাদিত সমান্তরাল হওয়া দরকার বলে আমি বলিনি
ক্যাম-হোয়াইট

2

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


এটি কার্যকর হতে পারে তবে কেসটি 8 টি বিটের একটি গ্রুপের মাধ্যমে এবং অন্য দলের মধ্যে সমস্তভাবে প্রচারের ক্ষেত্রে বিবেচনা করুন। ক্যারি প্রচার না করে তা নিশ্চিত করার জন্য উত্তরের উত্তরের (এমএসবি বা প্রথমে কিছু সেট করার) কৌশলটি সম্ভবত এটি যতটা সম্ভব দক্ষ। বীট করার বর্তমান লক্ষ্য (যেমন ভাল অ-লুপিং শাখাবিহীন উত্তর) হ'ল 5 টি রিস্ক-ভি এসএম এএলইউ নির্দেশনা সহ নির্দেশ-স্তরের সমান্তরালতা সমালোচনামূলক পথে কেবল 3 টি চক্র তৈরি করে, এবং দুটি 64-বিট ধ্রুবক ব্যবহার করে।
পিটার কর্ডেস

0

প্রতিটি বাইটে সম্পূর্ণ একা ফোকাস করুন, তারপরে এটি যেখানে ছিল সেখানে রেখে দিন।

uint64_t sub(uint64_t arg) {
   uint64_t res = 0;

   for (int i = 0; i < 64; i+=8) 
     res += ((arg >> i) - 1 & 0xFFU) << i;

    return res;
   }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.