আপনি খুব উচ্চ মানের যদৃচ্ছতা প্রয়োজন না থাকে, তাহলে এবং বন্ধ-টু-সমবন্টন যথেষ্ট ভাল, আপনি যেতে পারেন সত্যিই দ্রুত, বিশেষত SSE2 বা AVX2 সঙ্গে এক্স 86 মত দক্ষ SIMD পূর্ণসংখ্যা ভেক্টর একটি আধুনিক CPU তে।
এটি @ নোমিনালঅ্যানিমালের উত্তরের মত, যেহেতু আমাদের দুজনেরই একই ধারণা ছিল তবে x86 এর জন্য ম্যানুয়ালি ভেক্টরাইজড। (এবং আরও খারাপ মানের এলোমেলো সংখ্যার সাথে, তবে এখনও প্রচুর ব্যবহারের ক্ষেত্রে এটি যথেষ্ট ভাল)) এটি 2.5 ন্যূনগা হার্জ ইনটেল হ্যাসওয়েলে ASCII আউটপুটটির N 13 গিগাবাইট / এস-এ নোমিনালের কোডের চেয়ে প্রায় 15 বা 30 গুণ বেশি দ্রুত গতিতে চলেছে This এভিএক্স 2 সহ সিপিইউ। এটি তাত্ত্বিক সর্বোচ্চ প্রধান মেমরি মেমরি ব্যান্ডউইদথের চেয়ে কম (দ্বৈত চ্যানেল ডিডিআর ৩3-১00০০ প্রায় ২৫.GB গিগাবাইট / সে) তবে আমি / দেব / নালকে লেখার সময় দিচ্ছিলাম যাতে এটি কেবল ক্যাশে গরম থাকে এমন একটি বাফার পুনরায় লেখার জন্য। স্কাইলেকের এই একই কোডটি হাসওয়ালের তুলনায় উল্লেখযোগ্যভাবে দ্রুত চালানো উচিত (এই উত্তরটির নীচে দেখুন)।
ধরে নিই যে আপনি আসলে আই / ও-তে ডিস্কে বাধা দিচ্ছেন বা এটিকে কোথাও পাইপ দিচ্ছেন, দ্রুত প্রয়োগের অর্থ আপনার সিপিইউ এমনকি নিষ্ক্রিয়ের চেয়ে বেশি ঘড়িও নিতে হবে না। ফলাফলটি তৈরি করতে এটি মোট কম শক্তি ব্যবহার করে। (ব্যাটারির জীবন / তাপ / গ্লোবাল ওয়ার্মিং।
এটি এত দ্রুত যে আপনি সম্ভবত এটি ডিস্কে লিখতে চান না। কেবল প্রয়োজন অনুসারে পুনরায় জেনারেট করুন (যদি আপনি আবার একই ডেটা চান তবে একই বীজ থেকে)। এমনকি আপনি যদি এটি কোনও বহু-থ্রেড প্রক্রিয়াতে ফিড করতে চান যা সমস্ত সিপিইউ ব্যবহার করতে পারে তবে ডেটা এটিতে পাইপ করার জন্য এটি চালানো L3 ক্যাশে গরম থাকবে (এবং এটিতে লেখা L2 ক্যাশে যেটি লিখেছিল) এবং খুব ব্যবহার করুন সামান্য সিপিইউ সময়। (তবে নোট করুন যে /dev/null
পাইপিংয়ের ফলে বনাম প্রচুর ওভারহেড যুক্ত হয়েছে Sk একটি স্কাইলেক আই --kk০০ কে-তে পাইপিং wc -c
বা অন্য কোনও প্রোগ্রাম যা কেবল পড়ে + এটির ইনপুট বাতিল করে দেয়, এটি লেখার চেয়ে প্রায় ৮ গতি কম/dev/null
এবং কেবলমাত্র a০ % ব্যবহার করে সিপিইউ।কিন্তু এটি এখনও 3.9 গিগাহার্টজ সিপিইউতে 4.0 জিবি / এস।
এটি একটি দ্রুত পিসিআই-সংযুক্ত এসএসডি থেকে পুনরায় পড়ার চেয়ে দ্রুততর, তবে IDK যদি এটি আরও বেশি দক্ষ দক্ষ হয় (ভেক্টর-ইন্টিজার গুণকটি বেশ ব্যস্ত রাখা হয়, এবং এটি সম্ভবত অন্যান্য এভিএক্স 2 সহ বেশ শক্তিশালী-ক্ষুধার্ত হয়) 256 বি ভেক্টর এএলইউ)। ওও, আমি জানি না যে ডিস্ক থেকে পড়া সিপিইউ সময় এমন কিছু থেকে সরিয়ে ফেলবে যা এই ইনপুটটির প্রক্রিয়াকরণে সমস্ত কোরকে সর্বাধিক প্রসারিত করছে। আমি অনুমান করেছিলাম যে 128k অংশগুলিতে পুনরায় জেনারেট করার জন্য একটি প্রসঙ্গ-স্যুইচ ফাইল সিস্টেম / পেজক্যাচ কোড চালানো এবং ডিস্ক থেকে ডেটা পড়তে পৃষ্ঠাগুলি বরাদ্দের সাথে প্রতিযোগিতামূলক হতে পারে। অবশ্যই, যদি এটি ইতিমধ্যে পেজকেচে গরম থাকে তবে এটি মূলত মেমসিপি। ওও, আমরা ইতিমধ্যে ম্যাকপি হিসাবে দ্রুত সম্পর্কে লিখি! (যা পড়ার এবং লেখার মধ্যে প্রধান মেমরি ব্যান্ডউইথকে বিভক্ত করতে পারে)। (স্মরণে রাখুন যে লেখাটি নোট করুন 'rep movsb
(মাইক্রোকোডে অপ্টিমাইজড মেমকি এবং মেমসেট, যা আরএফওকে এড়িয়ে চলে, যেহেতু অ্যান্ডি গ্লিউ এর পি 6 (পেন্টিয়াম প্রো) ) এ প্রয়োগ করেছে )।
এখনও অবধি এটি কেবল ধারণার প্রমাণ এবং নতুন লাইন হ্যান্ডলিংটি কেবল প্রায় সঠিক। পাওয়ার-অফ -২ বাফারের প্রান্তের চারপাশে এটি ভুল। আরও উন্নয়নের সময় সহ। আমি নিশ্চিত যে আমি নিউলাইনগুলি সুনির্দিষ্টভাবে সঠিকভাবে সন্নিবেশ করানোর আরও কার্যকর উপায় খুঁজে পেতে পারি, ওভারহেড কমপক্ষে এটির চেয়ে কম হিসাবে (কেবলমাত্র ফাঁকা স্থানের তুলনায়)। আমি মনে করি এটি 10 থেকে 20% এর মতো। আমি কেবল এই রানটি কীভাবে তৈরি করতে পেরেছি তা জানতে আগ্রহী, আসলে এটির কোনও পালিশ সংস্করণ নেই, তাই আমি সেই অংশটি পাঠকের জন্য অনুশীলন হিসাবে ছেড়ে দেব, মন্তব্যগুলিতে কিছু ধারণার বর্ণনা দিয়ে।
একটি হাইওয়েল আই 5 এর 2.5GHz সর্বোচ্চ টার্বোতে , DDR3-1600MHz র্যাম সহ সময়সীমার 100GiB উত্পাদন করে তবে নিচে মজুত হয়। (জিসিসি ৫.৪ সহ উইন ১০-এ সাইগউইন 64৪ এর সময়সীমা -O3 -march=native
বাদ দেওয়া -funroll-loops
হয়েছে , যেহেতু এই ধার করা ল্যাপটপটিতে শালীন সময় চালানোর জন্য আমার যথেষ্ট সময় ব্যয় হচ্ছিল on ইউএসবিতে কেবল লিনাক্স বুট করা উচিত ছিল)।
অন্যথায় সুনির্দিষ্ট না করাতে / dev / নালকে লেখা।
- জেমস হলিস এর: (পরীক্ষিত নয়)
- নামমালার লেখার সংস্করণ: ~ 2.21
- এটি (এসএসই 2): ~ 0.142s (আনসাইক্লড টাইমস = রিয়েল = 14.232 এস, ইউজার = 13.999 এস, সিএস = 0.187)।
- এটি (AVX-128): ~ 0.140s
- এটি (এভিএক্স 2): ~ 0.073 এস (আনস্কেলড: রিয়েল = 0 এম 7.291 এস, ইউজার = 0 এম 7.125 সে, সিএস = 0 এম0.155 এস)।
- এটিতে (এভিএক্স 2) সাইগউইন পাইপ করছে
wc -c
, 128kiB বাফার আকারের সাথে: 0.32s সিপিইউ সহ 2.38GHz (সর্বোচ্চ ডুয়াল-কোর টার্বো)। (অনাবৃত সময়: আসল = 32.466 এর ব্যবহারকারী = 11.468 এস সিএস = 41.092, এটি এবং উভয়ই সহ wc
)। যদিও কেবলমাত্র অর্ধেক ডেটা অনুলিপি করা হয়েছিল, কারণ আমার নির্বোধ প্রোগ্রামটি ধরে নিয়েছে যে লেখাই পুরো বাফার করে, যদিও এটি ঘটনাটি নয় এবং সাইগউইন () কেবল পাইপে কল করে call৪ কে করে।
সুতরাং এসএসই 2 এর সাথে এটি @ নামমাল অ্যানিমাল এর স্কেলার কোডের চেয়ে 15 গুণ বেশি গতিযুক্ত। এভিএক্স 2 সহ এটি প্রায় 30 গুণ বেশি দ্রুত। আমি নমিনালের কোডটির কোনও সংস্করণ চেষ্টা করিনি যা কেবলমাত্র write()
পরিবর্তে ব্যবহার করে fwrite()
, তবে সম্ভবত বড় বাফারদের জন্য স্টডিও বেশিরভাগ ক্ষেত্রেই বাইরে থাকে। যদি এটি ডেটা অনুলিপি করে থাকে তবে এতে প্রচুর মন্দা পড়বে।
টাইমস একটি কোর 2 ডুও ই 6600 (মেরোম 2.4GHz, 32kiB প্রাইভেট এল 1, 4 এমআইবি শেয়ারকৃত এল 2 ক্যাশে), ডিডিআর 2-533 এমএইচজেডে 64 বিট লিনাক্স 4.2 (উবুন্টু 15.10) তে 1 জিবি ডেটা উত্পাদন করবে । এখনও লেখার জন্য একটি 128kiB বাফার আকার ব্যবহার করে (), সেই মাত্রাটি অন্বেষণ করেনি।
অন্যথায় সুনির্দিষ্ট না করাতে / dev / নালকে লেখা।
- (এসএসই ২) এটিকে নিউলাইন হ্যান্ডলিং এবং এলোমেলো বাইটগুলির প্রতিটি ভেক্টর থেকে 4 টি সংখ্যার ভেক্টর সহ: 0.183s (18.3s এ 100GiB করার সময়সই হয়েছে, তবে 1GiB রানের জন্য অনুরূপ ফলাফল)। চক্র প্রতি 1.85 নির্দেশাবলী।
- (এসএসই 2) এটিতে পাইপ করুন
wc -c
: 0.593 এস (আনসিল্ডড: রিয়েল = 59.266 এস ইউজার = 20.148 এস সিএস = 1 এম 6.548 এস, ডাব্লিউসি এর সিপিইউ সময় সহ)। সাইগউইনের মতো একই সংখ্যক রাইটিং () সিস্টেম কল করে তবে আসলে সমস্ত ডেটা পাইপ করা হয় কারণ লিনাক্স একটি পাইপে সমস্ত 128k রাইটিং () র পরিচালনা করে।
- নোমিনালঅ্যানিমালের
fwrite()
সংস্করণ (gcc5.2 -O3 -march=native
), ./decdig 100 $((1024*1024*1024/200)) > /dev/null
প্রতি চক্রের 1.40 নির্দেশ সহ : 3.19s +/- 0.1% দিয়ে চালিত । -ফুনরোল-লুপগুলি সম্ভবত একটি ছোট পার্থক্য করেছে। clang-3.8 -O3 -march=native
: 3.42 এস +/- 0.1%
- নামমাত্র- এতে
fwrite
পাইপিং করা হচ্ছে wc -c
: আসল = 3.980 এর ব্যবহারকারী = 3.176 এস সিএস = 2.080 এস
- জেমস হোলিস-এর-সময়ে-সময়ে-সংস্করণ (
clang++-3.8 -O3 -march=native
): 22.885 এস +/- 0.07%, প্রতি চক্রের 0.84 নির্দেশাবলীর সাথে। (g ++ 5.2 সামান্য ধীর ছিল: 22.98 সে)। একবারে কেবল একটি পংক্তি লেখা সম্ভবত উল্লেখযোগ্যভাবে আঘাত করেছে।
- স্টাফেন
tr < /dev/urandom | ...
চ্যাজেলাসের: আসল = 41.430 সে ব্যবহারকারী = 26.832 এস সিএস = 40.120 এস। tr
বেশিরভাগ সময় নিজের কাছে সিপিইউ কোর পেয়ে যাচ্ছিল, প্রায় সমস্ত সময় কার্নেল ড্রাইভারের মধ্যে এলোমেলো বাইট তৈরি করে এবং একটি পাইপে অনুলিপি করে ব্যয় করে। এই ডুয়াল কোর মেশিনের অন্যান্য কোরটি বাকী পাইপলাইনটি চালাচ্ছিল।
time LC_ALL=C head -c512M </dev/urandom >/dev/null
: অর্থাত কেবল পাইপিংয়ের সাথে এতটা এলোমেলোভাবে পড়া: বাস্তব = 35.018 এর ব্যবহারকারী = 0.036 এস সিএস = 34.940 এস।
- লু ভান ফ্যাকের পার্ল প্রোগ্রাম (উবুন্টু 15.10 থেকে পার্ল ভি 5.20.2)
LANG=en_CA.UTF-8
: রিয়েল = 4 এম 32.634 এস ব্যবহারকারী = 4 এম 3.288 এস সিএস = 0 এম 29.364।
LC_ALL=C LANG=C
: আসল = 4m18.637s ব্যবহারকারী = 3m50.324 sys = 0m29.356s। এখনও খুব ধীর।
- (এসএসই 2) এটি কোনও নিউলাইন হ্যান্ডলিং ছাড়াই , এবং এলোমেলো বাইটগুলির প্রতিটি ভেক্টর থেকে 3 বা 4 সংখ্যক ভেক্টর (প্রায় ঠিক একই গতি:
dig3 = v%10
পদক্ষেপটি এই এইচডাব্লুয়ের বিরতি-এমনকি প্রায়): 0.166 (প্রতিটি চক্র প্রতি 1.82 নির্দেশ) । নিখুঁতভাবে দক্ষ নিউলাইন হ্যান্ডলিংয়ের সাথে আমরা কী কাছে আসতে পারি তার মূলত এটি নিম্ন সীমা।
- (SSE2) কোন সম্পর্কে newline হ্যান্ডলিং সঙ্গে এই পুরোনো সংস্করণ, কিন্তু শুধুমাত্র ব্যবহার uint16_t উপাদান প্রতি এক অঙ্ক পেয়ে
v%10
, 0,222 সেকেন্ড +/- 0.4%, চক্র প্রতি 2.12 নির্দেশাবলী। (Gcc5.2, দিয়ে সংকলিত -march=native -O3 -funroll-loops
। এই হার্ডওয়্যারটিতে এই কোডটির জন্য আনলোল লুপগুলি ঘটবে। এটি অন্ধভাবে ব্যবহার করবেন না, বিশেষত বড় প্রোগ্রামগুলির জন্য)।
- (এসএসই 2) এর পুরানো সংস্করণ, কোনও ফাইলকে লিখিত (3 দ্রুত চৌম্বকীয় হার্ড ড্রাইভের একটি RAID10f2 এ, লেখার জন্য খুব অনুকূল নয়): ~ 4 সেকেন্ড। লেখার আগে () ব্লক করার আগে অনেক বেশি নোংরা ডেটা মঞ্জুর করার জন্য কার্নেল I / O বাফার সেটিংস টুইট করে দ্রুত যেতে পারে। "সিস্টেম" সময় এখনও "ব্যবহারকারী" সময়ের চেয়ে অনেক বেশি ~ 1.0 সেকেন্ড than ধীরে ধীরে DDR2-533 র্যাম সহ এই পুরানো সিস্টেমে, কার্নেলটি পৃষ্ঠার ক্যাশে ডেটা ম্যাকপি করতে এবং এক্সএফএস ফাংশন চালাতে ~ 4x আর বেশি সময় নেয় এটি আমার লুপের জন্য গরম জায়গায় থাকে এমন একটি বাফারে পুনরায় লেখার জায়গায় রাখে does ক্যাশে।
কিভাবে এটা হলো
একটি দ্রুত পিআরএনজি স্পষ্টতই অপরিহার্য। xorshift128 + ভেক্টরাইজ করা যেতে পারে, তাই আপনার কাছে সিমড ভেক্টরের উপাদানগুলিতে সমান্তরালে দুটি বা চার 64-বিট জেনারেটর রয়েছে। প্রতিটি পদক্ষেপ এলোমেলো বাইটের একটি সম্পূর্ণ ভেক্টর উত্পাদন করে। ( 256 বি এভিএক্স 2 বাস্তবায়ন এখানে ইন্টেল অভ্যন্তরীণ সাথে )। আমি নামমিনালের পছন্দের xorshift * এর উপরে এটি বেছে নিয়েছি, কারণ -৪-বিট ভেক্টর পূর্ণসংখ্যার গুণ কেবল এসএসই 2 / এভিএক্স 2 এ বর্ধিত-নির্ভুলতার কৌশলগুলির সাহায্যে সম্ভব ।
এলোমেলো বাইটের ভেক্টর দেওয়া, আমরা প্রতিটি 16-বিট উপাদান একাধিক দশমিক অঙ্কে কাটাতে পারি। আমরা 16-বিট উপাদানগুলির একাধিক ভেক্টর উত্পাদন করি যা প্রতিটি ASCII ডিজিট + ASCII স্পেস । আমরা এটি সরাসরি আমাদের আউটপুট বাফারে সংরক্ষণ করি।
আমার আসল সংস্করণটি কেবল x / 6554
কোনও ভেক্টরের প্রতিটি uint16_t উপাদান থেকে একটি এলোমেলো অঙ্ক পেতে ব্যবহৃত হয়েছিল। এটি সর্বদা 0 এবং 9 এর মধ্যে থাকে lusive এটি পক্ষপাতদুষ্ট দূরে 9
, কারণ (2^16 -1 ) / 6554
এটি শুধুমাত্র 9.99923 is (6554 = সিল ((2 ^ 16-1) / 10)) যা নিশ্চিত করে যে ভাগফল সবসময় <10)
x/6554
একটি "যাদু" ধ্রুবক ( স্থির-পয়েন্ট পারস্পরিক ) এবং উচ্চ-অর্ধ ফলাফলের ডান শিফট দ্বারা এক গুণে গুণ করা যায় । এটি একটি ধ্রুবক দ্বারা বিভাগের জন্য সেরা কেস; কিছু বিভাজনকারী আরও ক্রিয়াকলাপ গ্রহণ করে এবং স্বাক্ষরিত বিভাগে অতিরিক্ত কাজ লাগে। x % 10
একটি অনুরূপ পক্ষপাত আছে এবং গণনা করা হিসাবে সস্তা নয়। (জিসিসির এসএম আউটপুট সমতুল্য x - 10*(x/10)
, যেমন একটি অতিরিক্ত গুণক এবং একটি মডুলার গুণক বিপরীত ব্যবহার করে বিভাগের শীর্ষে বিয়োগ করা হবে)) এছাড়াও, xorshift128 + এর সর্বনিম্ন বিটটি উচ্চ মানের নয় , তাই উচ্চ বিট থেকে এনট্রপি নিতে ভাগ করা ভাল ( কম বিট থেকে এন্ট্রপি নেওয়ার জন্য মডুলোর চেয়ে মানের পাশাপাশি গতির জন্য)।
তবে আমরা @ নোমিনালের digit()
ফাংশনের মতো স্বল্প দশমিক সংখ্যা দেখে প্রতিটি uint16_t এ আরও বেশি এনট্রপি ব্যবহার করতে পারি । সর্বোচ্চ পারফরম্যান্সের জন্য, আমি কম 3 দশমিক সংখ্যা নেওয়ার সিদ্ধান্ত নিয়েছি এবং x/6554
একটি পিএমএলএলডাব্লু এবং পিএসইউবিডু (এবং সম্ভবত কিছু এমওভিডিকিএ) বনাম 4 টি কম দশমিক সংখ্যার উচ্চ মানের মানের বিকল্পটি সংরক্ষণ করতে চাই। এক্স / 6554 নিম্ন 3 দশমিক সংখ্যা দ্বারা সামান্য প্রভাবিত হয়, সুতরাং একই উপাদান থেকে অঙ্কগুলির মধ্যে কিছু পারস্পরিক সম্পর্ক রয়েছে (ভেক্টরের প্রস্থের উপর নির্ভর করে ASCII আউটপুটে 8 বা 16 অঙ্কের বিভাজন)।
আমি মনে করি জিসিসিটি একটি দীর্ঘ চেইনের পরিবর্তে ১০ দ্বারা এবং এক দ্বারা 1000 দ্বারা বিভক্ত হয়ে গেছে, সুতরাং এটি সম্ভবত নন-লুপ বহনকারী নির্ভরতা শৃঙ্খলার দৈর্ঘ্যটি ছোট করে না যা প্রতিটি পিআরএনজি আউটপুট থেকে 4 টি ফলাফল দেয় produces পোর্ট0 (ভেক্টর মাল্টিপল এবং শিফট) হ'ল মডুলার গুণিত বিপরীত কারণ, এবং জোরশিফ্ট + এ পরিবর্তনগুলি কারণ এটি একটি ভেক্টর-গুণকে বাঁচাতে অবশ্যই কার্যকর।
xorshift + এত দ্রুত যে এমনকি প্রতি 16 টি (অর্থাৎ 20% দক্ষতা) থেকে কেবল only 3.3 বিট ব্যবহার করে এটিকে একাধিক দশমিক সংখ্যায় কাটানোর চেয়ে খুব ধীর নয়। আমরা কেবল ইউনিফর্ম বিতরণ আনুমানিক, কারণ গুণমান খুব খারাপ না হওয়া পর্যন্ত এই উত্তরটি গতির দিকে নিবদ্ধ থাকে isn't
যে কোনও ধরণের শর্তাধীন আচরণ যা ভেরিয়েবল সংখ্যক উপাদানকে রাখে আরও বেশি কাজ করতে পারে। (তবে সম্ভবত সিমড বাম-প্যাকিং কৌশলগুলি ব্যবহার করে কিছুটা দক্ষতার সাথে করা যেতে পারে However তবে এটি ছোট উপাদান আকারের জন্য কম দক্ষ হয়ে ওঠে; জায়ান্ট শেফেল-মাস্ক লকিং টেবিলগুলি কার্যকর নয়, এবং 32- এর চেয়ে কম সংখ্যক অ্যাভিএক্স 2 লেন-ক্রসিং শ্যাফেল নেই বিট উপাদানসমূহ: একটি 128 বি পিএসএইচএফবি সংস্করণটি বিএমআই 2 পেক্সট / পিডিইপি সহ ফ্লাইতে একটি মাস্ক তৈরি করতে সক্ষম হতে পারে , আপনি যেমন বড় আকারের অ্যাভিএক্স 2 এর জন্য পারেন তবে এটি মুশকিল কারণ 64-বিট পূর্ণসংখ্যায় কেবল 8 বাইট থাকে god গডবোল্ট লিঙ্ক এই উত্তরে এমন কিছু কোড রয়েছে যা উচ্চতর উপাদানগুলির জন্য কাজ করতে পারে))
আরএনজির বিলম্ব যদি একটি বাধা হয়ে থাকে তবে আমরা দুটি জেনারেটরের সমান্তরালে চালিয়ে আরও দ্রুত যেতে পারতাম, আমরা কোনটি ব্যবহার করি তা পরিবর্তিত করে। সংকলকটি এখনও অনিবন্ধিত লুপে নিবন্ধগুলিতে সহজেই সবকিছু রাখতে পারে এবং এটি দুটি নির্ভরশীল শৃঙ্খলাগুলিকে সমান্তরালে চালাতে দেয়।
বর্তমান সংস্করণে, পিআরএনজির আউটপুট কেটে, আমরা আসলে পোর্ট 0 থ্রুপুটটিতে পিআরএনজি ল্যাটেন্সি নয়, তবে তার প্রয়োজন নেই।
কোড: এভিএক্স 2 সংস্করণ
গডবোল্ট সংকলক এক্সপ্লোরারের আরও মন্তব্য সহ পূর্ণ সংস্করণ ।
খুব পরিপাটি নয়, দুঃখিত আমাকে ঘুমাতে হবে এবং এই পোস্টটি পেতে চাই।
SSE2 সংস্করণ পেতে থেকে s/_mm256/_mm
, s/256/128/
, s/v16u/v8u/
, এবং পরিবর্তন vector_size(32)
এছাড়াও 4 * 16 4 * 8 থেকে সম্পর্কে newline বৃদ্ধি পরিবর্তন 16. করতে। (যেমনটি আমি বলেছিলাম, কোডটি অগোছালো, এবং দুটি সংস্করণ সংকলনের জন্য ভালভাবে সেট আপ করা হয়নি AV মূলত একটি এভিএক্স 2 সংস্করণ তৈরি করার পরিকল্পনা করেনি, তবে তখন আমি সত্যিই আমার অ্যাক্সেস পেয়েছি এমন একটি হ্যাসওয়েল সিপিইউতে পরীক্ষা করতে চেয়েছিলাম))
#include <immintrin.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
//#include <string.h>
// This would work equally fast 128b or 256b at a time (AVX2):
// https://stackoverflow.com/questions/24001930/avx-sse-version-of-xorshift128
struct rngstate256 {
__m256i state0;
__m256i state1;
};
static inline __m256i xorshift128plus_avx2(struct rngstate256 *sp)
{
__m256i s1 = sp->state0;
const __m256i s0 = sp->state1;
sp->state0 = s0;
s1 = _mm256_xor_si256(s1, _mm256_slli_epi64(s1, 23));
__m256i state1new = _mm256_xor_si256(_mm256_xor_si256(_mm256_xor_si256(s1, s0),
_mm256_srli_epi64(s1, 18)),
_mm256_srli_epi64(s0, 5));
sp->state1 = state1new;
return _mm256_add_epi64(state1new, s0);
}
// GNU C native vectors let us get the compiler to do stuff like %10 each element
typedef unsigned short v16u __attribute__((vector_size(32)));
__m256i* vec_store_digit_and_space(__m256i vec, __m256i *restrict p)
{
v16u v = (v16u)vec;
v16u ten = (v16u)_mm256_set1_epi16(10);
v16u divisor = (v16u)_mm256_set1_epi16(6554); // ceil((2^16-1) / 10.0)
v16u div6554 = v / divisor; // Basically the entropy from the upper two decimal digits: 0..65.
// Probably some correlation with the modulo-based values, especially dig3, but we do this instead of
// dig4 for more ILP and fewer instructions total.
v16u dig1 = v % ten;
v /= ten;
v16u dig2 = v % ten;
v /= ten;
v16u dig3 = v % ten;
// dig4 would overlap much of the randomness that div6554 gets
const v16u ascii_digitspace = (v16u)_mm256_set1_epi16( (' '<<8) | '0');
v16u *vecbuf = (v16u*)p;
vecbuf[0] = div6554 | ascii_digitspace;
vecbuf[1] = dig1 | ascii_digitspace;
vecbuf[2] = dig2 | ascii_digitspace;
vecbuf[3] = dig3 | ascii_digitspace;
return p + 4; // always a constant number of full vectors
}
void random_decimal_fill_buffer(char *restrict buf, size_t len, struct rngstate256 *restrict rngstate)
{
buf = __builtin_assume_aligned(buf, 32);
// copy to a local so clang can keep state in register, even in the non-inline version
// restrict works for gcc, but apparently clang still thinks that *buf might alias *rngstate
struct rngstate256 rng_local = *rngstate;
__m256i *restrict p = (__m256i*restrict)buf;
__m256i *restrict endbuf = (__m256i*)(buf+len);
static unsigned newline_pos = 0;
do {
__m256i rvec = xorshift128plus_avx2(&rng_local);
p = vec_store_digit_and_space(rvec, p); // stores multiple ASCII vectors from the entropy in rvec
#if 1
// this is buggy at the end or start of a power-of-2 buffer:
// usually there's a too-short line, sometimes a too-long line
const unsigned ncols = 100;
newline_pos += 4*16;
if (newline_pos >= ncols) {
newline_pos -= ncols;
char *cur_pos = (char*)p;
*(cur_pos - newline_pos*2 - 1) = '\n';
}
#endif
// Turning every 100th space into a newline.
// 1) With an overlapping 1B store to a location selected by a counter. A down-counter would be more efficient
// 2) Or by using a different constant for ascii_digitspace to put a newline in one element
// lcm(200, 16) is 400 bytes, so unrolling the loop enough to produce two full lines makes a pattern of full vectors repeat
// lcm(200, 32) is 800 bytes
// a power-of-2 buffer size doesn't hold a whole number of lines :/
// I'm pretty sure this can be solved with low overhead, like maybe 10% at worst.
} while(p <= endbuf-3);
*rngstate = rng_local;
}
#define BUFFER_SIZE (128 * 1024)
const static size_t bufsz = BUFFER_SIZE;
__attribute__((aligned(64))) static char static_buf[BUFFER_SIZE];
int main(int argc, char *argv[])
{
// TODO: choose a seed properly. (Doesn't affect the speed)
struct rngstate256 xorshift_state = {
_mm256_set_epi64x(123, 456, 0x123, 0x456),
_mm256_set_epi64x(789, 101112, 0x789, 0x101112)
};
for (int i=0; i < 1024ULL*1024*1024 / bufsz * 100; i++) {
random_decimal_fill_buffer(static_buf, bufsz, &xorshift_state);
size_t written = write(1, static_buf, bufsz);
(void)written;
//fprintf(stderr, "wrote %#lx of %#lx\n", written, bufsz);
}
}
জিসিসি, ঝনঝন, বা আইসিসি (অথবা আশা করি যে কোনও অন্য সংকলক যা সি 99 এর জিএনইউ সি ডায়ালেক্ট এবং ইন্টেলের অভ্যন্তরীণগুলি বোঝে) দিয়ে সংকলন করুন। জিএনইউ সি ভেক্টর এক্সটেনশানগুলি মডিউলার গুণক বিপরীত ব্যবহার করে বিভাগ / মডুলোর জন্য যাদু সংখ্যাগুলি তৈরি করতে সংকলকটি পেতে খুব সুবিধাজনক এবং মাঝে মাঝে __attribute__
গুলি দরকারী।
এটি বহনযোগ্যভাবে লেখা যেতে পারে, তবে এটি আরও কোড লাগবে।
পারফরম্যান্স নোট:
নতুন লাইনগুলি সন্নিবেশ করানোর জন্য ওভারল্যাপিং-স্টোরটি কোথায় রাখবে তা স্থির করার জন্য গুরুত্বপূর্ণ শাখার ওভারহেড রয়েছে (শাখার ভুল ধারণা এবং কোর 2-এ সামনের বাধা), তবে স্টোর নিজেই কার্য সম্পাদনে কোনও প্রভাব ফেলেনি। সংকলকের অ্যাসেমের কেবল সেই স্টোর নির্দেশনা সম্পর্কে মন্তব্য করা (সমস্ত ব্রাঞ্চিং একই রেখে) একই সময়ে বার বার রান করা +1- এর চেয়ে কম 1% করার সাথে সাথে কোর 2 এর পারফরম্যান্সটি পুরোপুরি অপরিবর্তিত রয়েছে। সুতরাং আমি এই সিদ্ধান্তে পৌঁছেছি যে স্টোর বাফার / ক্যাশে এটি ঠিকঠাকভাবে পরিচালনা করে।
তবুও, ascii_digitspace
একটি নতুন লাইনের সাথে থাকা একটি উপাদানগুলির সাথে কোনও ধরণের ঘোরানো উইন্ডো ব্যবহার করা আরও দ্রুত হতে পারে, যদি আমরা পর্যাপ্ত তালিকাভুক্ত না করি যে কোনও কাউন্টার / ব্রাঞ্চিং চলে যায়।
/ Dev / নালকে লেখা মূলত একটি অপ-অপশন, তাই সম্ভবত বাফার সম্ভবত এল 2 ক্যাশে গরম থাকে (হাসওলে প্রতি 256kiB কোর প্রতি)। 128 বি ভেক্টর থেকে 256 বি ভেক্টরগুলির নিখুঁত গতিপথ আশা করা হচ্ছে: কোনও অতিরিক্ত নির্দেশ নেই এবং সমস্ত কিছু (স্টোর সহ) দ্বিগুণ প্রস্থের সাথে ঘটে। যদিও নতুন লাইন সন্নিবেশ শাখাটি প্রায় দ্বিগুণ নেওয়া হয়। দুর্ভাগ্যক্রমে আমি আমার অংশওয়াল সাইগউইন সেটআপে অংশটি শেষ করেছিলাম #ifdef
না।
হাসওলে অ্যাভিএক্স 2-স্টোর প্রতি 2.5GHz * 32B / 13.7GB / s = 5.84 চক্র। এটি বেশ ভাল, তবে দ্রুত হতে পারে। সাইগউইন সিস্টেমের কলগুলির মধ্যে কিছুটা ওভারহেড থাকতে পারে যা আমি ভাবলি। আমি সংকলকের এসএম আউটপুটে তাদের মন্তব্য করার চেষ্টা করিনি (যা নিশ্চিত করে যে কিছুই অপ্টিমাইজড হবে না।)
এল 1 ক্যাশে প্রতি ঘন্টার জন্য একটি 32 বি স্টোর ধরে রাখতে পারে, এবং এল 2 খুব কম ব্যান্ডউইদথ নয় (যদিও উচ্চতর বিলম্বিতা)।
আমি যখন কয়েকটি সংস্করণ আগে আইএসিএর দিকে নজর দিয়েছিলাম (নিউলাইনগুলির জন্য শাখা ছাড়াই, তবে কেবল আরএনজি ভেক্টর অনুসারে কেবল একটি এএসসিআইআই ভেক্টর পাচ্ছিল), এটি প্রতি 4 বা 5 ক্লকের জন্য 32 বি ভেক্টর স্টোরের মতো কিছু ভবিষ্যদ্বাণী করেছিল।
এসএম xog ট্যাগ উইকিতে আমি লিঙ্ক যুক্ত করেছি বলে অ্যাগ্রার ফগের গাইড এবং অন্যান্য অপ্টিমাইজেশন সংস্থান বিবেচনা করে আমি নিজেই এএসএমটি দেখার উপর ভিত্তি করে প্রতিটি আরএনজি ফলাফল থেকে আরও তথ্য আহরণের থেকে আরও দ্রুতগতি অর্জনের আশা করছিলাম ))
স্কাইলেকে এটি সম্ভবত উল্লেখযোগ্যভাবে দ্রুততর হবে , যেখানে ভেক্টর ইন্টিজার মাল্টিপল এবং শিফটটি হ্যাসওয়েলের (কেবলমাত্র পি 0) তুলনায় দ্বিগুণ বহু পোর্ট (পি 0 / পি 1) চালাতে পারে। xorshift এবং অঙ্ক এক্সট্রাকশন উভয়ই অনেকগুলি শিফট এবং গুণক ব্যবহার করে। ( আপডেট: স্কাইলেক এটিকে 3.02 আইপিসিতে চালায়, আমাদের 32 জি বাইট এভিএক্স 2 স্টোর প্রতি 3.77 চক্র প্রদান করে , 1 জিবি পুনরাবৃত্তি প্রতি 0.030 সেকেন্ডে টাইম /dev/null
করে লিনাক্স 4.15 এ i7-6700k এ 3.9GHz লিখে লিখেছেন)।
এটি ভাল কাজ করতে 64-বিট মোডের প্রয়োজন হয় না । সংকলিত করার সময় এসএসই 2 সংস্করণটি ঠিক তত দ্রুত -m32
, কারণ এর জন্য খুব বেশি ভেক্টর নিবন্ধের প্রয়োজন হয় না, এবং সমস্ত 64-বিট গণিত ভেক্টরগুলিতে সম্পন্ন হয়, সাধারণ উদ্দেশ্যে নিবন্ধভুক্ত নয়।
এটি কোর 2-তে 32-বিট মোডে আসলে কিছুটা দ্রুত, কারণ তুলনা / শাখা ম্যাক্রো-ফিউশন কেবল 32-বিট মোডে কাজ করে, সুতরাং আউট-অফ-অর্ডার কোর (18.3s (1.85 প্রতি ঘড়ির প্রতি নির্দেশ)) বনাম কম ইউপ রয়েছে 16 16.9s (2.0 আইপিসি)। আরএক্স প্রিফিক্স না থাকা থেকে ছোট কোডের আকারটি কোর 2 এর ডিকোডারগুলিকে সহায়তা করে।
এছাড়াও, কিছু রেগ-রেজি ভেক্টর পদক্ষেপগুলি লোডের সাথে প্রতিস্থাপন করা হয়, যেহেতু সমস্ত ধ্রুবকগুলি ভেক্টর রেগুলিতে আর ঠিক করে না। যেহেতু এল 1 ক্যাশে থেকে লোড থ্রুপুট কোনও বাধা নয়, এটি আসলে সহায়তা করে। (যেমন set1(10)
: ধ্রুবক ভেক্টর দ্বারা গুণমান : movdqa xmm0, xmm10
/ / pmullw xmm0, xmm1
রূপান্তরিত হয় movdqa xmm0, [constant]
/ pmullw xmm0, xmm1
)) যেহেতু রেজি-রেজি মোভডকিউএ একটি ALU বন্দর প্রয়োজন, এটি আসল কাজটি সম্পন্ন করার সাথে প্রতিযোগিতা করে, তবে একটি এমওভিডিকিউএ লোড কেবল ফ্রন্ট-এন্ড ডিকোড ব্যান্ডউইথের জন্য প্রতিযোগিতা করে। (অনেক নির্দেশাবলীর মধ্যে একটি 4-বাইট ঠিকানা থাকলে REX উপসর্গগুলি সংরক্ষণ করে প্রচুর লাভ বাতিল হয়ে যায়।
যদি আ’লু মোভডকিউএ উপস সংরক্ষণ করা হয় তবে আসল লাভটি কোথা থেকে এসেছে তা আমি অবাক হব না, যেহেতু সীমানাটি 2.0 আইপিসির গড়ের সাথে রাখা উচিত।
এই সমস্ত পার্থক্যগুলি হ্যাসওয়েলে অদৃশ্য হয়ে যায়, যেখানে লুপব্যাক বাফার না হলে পুরো জিনিসটি ডিকোডড-ইউওপ ক্যাশে থেকে চালানো উচিত। ALU + শাখা ম্যাক্রো-ফিউশন উভয় মোডে কাজ করে নেহালেমের পরে।