x86-64 মেশিন কোড, 34 বাইট
কলিং কনভেনশন = x86-64 সিস্টেম ভি x32 এবিআই (দীর্ঘ মোডে 32-বিট পয়েন্টার সহ আর্কগুলি নিবন্ধ করুন)।
ফাংশন স্বাক্ষর হয় void stewie_x87_1reg(float *seq_buf, unsigned Nterms);। ফাংশনটি অ্যারের প্রথম দুটি উপাদানগুলিতে x0 এবং x1 বীজের মান গ্রহণ করে এবং ক্রমটি কমপক্ষে আরও N উপাদানগুলিতে প্রসারিত করে। বাফারটি 2 + এন-গোলাকৃত-আপ-থেকে-পরবর্তী-একাধিক-4-এ প্যাড করতে হবে। (যেমন 2 + ((N+3)&~3), বা কেবল এন + 5)।
উচ্চ-কর্মক্ষমতা বা সিমডি-ভেক্টরাইজড ক্রিয়াকলাপগুলির জন্য সমাবেশে প্যাডযুক্ত বাফারগুলির প্রয়োজন স্বাভাবিক, এবং এই অনিবন্ধিত লুপটি একই রকম, তাই আমি মনে করি না এটি নিয়মগুলি খুব বেশি বাঁকানো। কলার সহজেই (এবং হওয়া উচিত) সমস্ত প্যাডিং উপাদানগুলিকে উপেক্ষা করতে পারে।
বাফারে ইতিমধ্যে নেই এমন একটি ফাংশন আর্গ হিসাবে x0 এবং x1 পাস করার জন্য আমাদের কেবলমাত্র 3 বাইট (এক movlps [rdi], xmm0বা জন্য movups [rdi], xmm0) ব্যয় করতে হবে, যদিও এটি সিস্টেমের ভি struct{ float x,y; };পৃথক দুটি পৃথক এক্সএমএম রেজিস্টারে পাস হওয়ার কারণে এটি একটি মানক নয় calling
এটি objdump -drw -Mintelমন্তব্য যুক্ত করার জন্য কিছুটা ফর্ম্যাটেটিং দিয়ে আউটপুট
0000000000000100 <stewie_x87_1reg>:
;; load inside the loop to match FSTP at the end of every iteration
;; x[i-1] is always in ST0
;; x[i-2] is re-loaded from memory
100: d9 47 04 fld DWORD PTR [rdi+0x4]
103: d8 07 fadd DWORD PTR [rdi]
105: d9 57 08 fst DWORD PTR [rdi+0x8]
108: 83 c7 10 add edi,0x10 ; 32-bit pointers save a REX prefix here
10b: d8 4f f4 fmul DWORD PTR [rdi-0xc]
10e: d9 57 fc fst DWORD PTR [rdi-0x4]
111: d8 6f f8 fsubr DWORD PTR [rdi-0x8]
114: d9 17 fst DWORD PTR [rdi]
116: d8 7f fc fdivr DWORD PTR [rdi-0x4]
119: d9 5f 04 fstp DWORD PTR [rdi+0x4]
11c: 83 ee 04 sub esi,0x4
11f: 7f df jg 100 <stewie_x87_1reg>
121: c3 ret
0000000000000122 <stewie_x87_1reg.end>:
## 0x22 = 34 bytes
এই সি রেফারেন্স বাস্তবায়ন gcc -Osকিছুটা অনুরূপ কোডকে সংকলন করে (সহ )। জিসিসি একই কৌশলটিকে বেছে নিয়েছে, একটি রেজিস্টারে কেবল একটি পূর্ববর্তী মান রাখার জন্য।
void stewie_ref(float *seq, unsigned Nterms)
{
for(unsigned i = 2 ; i<Nterms ; ) {
seq[i] = seq[i-2] + seq[i-1]; i++;
seq[i] = seq[i-2] * seq[i-1]; i++;
seq[i] = seq[i-2] - seq[i-1]; i++;
seq[i] = seq[i-2] / seq[i-1]; i++;
}
}
আমি দ্বি-নিবন্ধের x87 সংস্করণ সহ কোড সহ অন্যান্য উপায়ে পরীক্ষা করেছিলাম:
; part of loop body from untested 2-register version. faster but slightly larger :/
; x87 FPU register stack ; x1, x2 (1-based notation)
fadd st0, st1 ; x87 = x3, x2
fst dword [rdi+8 - 16] ; x87 = x3, x2
fmul st1, st0 ; x87 = x3, x4
fld st1 ; x87 = x4, x3, x4
fstp dword [rdi+12 - 16] ; x87 = x3, x4
; and similar for the fsubr and fdivr, needing one fld st1
আপনি যদি গতিতে যাচ্ছিলেন তবে আপনি এটি এইভাবে করতেন (এবং এসএসই উপলব্ধ ছিল না)
একবারে প্রবেশের পরিবর্তে লুপের ভিতরে মেমরি থেকে বোঝা চাপানো হয়ত সাহায্য করতে পারে, যেহেতু আমরা কেবল সাব এবং ডিভ ফলাফলগুলি অর্ডার থেকে সঞ্চার করতে পারি, তবে এখনও প্রবেশের উপর স্ট্যাক স্থাপনের জন্য এটি দুটি এফএলডি নির্দেশিকা প্রয়োজন needs
আমি এসএসই / এভিএক্স স্কেলার গণিতটি ব্যবহার করার চেষ্টা করেছি (xmm0 এবং xmm1 এর মান দিয়ে শুরু করে) তবে বৃহত্তর নির্দেশের আকার হত্যাকারী। ব্যবহার করা addps(যেহেতু এটি 1B এর চেয়ে কম addss) একটি সামান্য বিটকে সহায়তা করে। অ-পরিবহনের নির্দেশাবলীর জন্য আমি অ্যাভিএক্স ভেক্স-প্রিফিক্স ব্যবহার করেছি, যেহেতু ভ্যাঙ্কসএসএসএসইবিপিএস (এবং এসইউবিএসএস-এর সমান দৈর্ঘ্য) এর চেয়ে এক বাইট বেশি দীর্ঘ।
; untested. Bigger than x87 version, and can spuriously raise FP exceptions from garbage in high elements
addps xmm0, xmm1 ; x3
movups [rdi+8 - 16], xmm0
mulps xmm1, xmm0 ; xmm1 = x4, xmm0 = x3
movups [rdi+12 - 16], xmm1
vsubss xmm0, xmm1, xmm0 ; not commutative. Could use a value from memory
movups [rdi+16 - 16], xmm0
vdivss xmm1, xmm0, xmm1 ; not commutative
movups [rdi+20 - 16], xmm1
এই পরীক্ষার জোতা দিয়ে পরীক্ষিত:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
int main(int argc, char**argv)
{
unsigned seqlen = 100;
if (argc>1)
seqlen = atoi(argv[1]);
float first = 1.0f, second = 2.1f;
if (argc>2)
first = atof(argv[2]);
if (argc>3)
second = atof(argv[3]);
float *seqbuf = malloc(seqlen+8); // not on the stack, needs to be in the low32
seqbuf[0] = first;
seqbuf[1] = second;
for(unsigned i=seqlen ; i<seqlen+8; ++i)
seqbuf[i] = NAN;
stewie_x87_1reg(seqbuf, seqlen);
// stewie_ref(seqbuf, seqlen);
for (unsigned i=0 ; i< (2 + ((seqlen+3)&~3) + 4) ; i++) {
printf("%4d: %g\n", i, seqbuf[i]);
}
return 0;
}
সঙ্গে সংকলন nasm -felfx32 -Worphan-labels -gdwarf2 golf-stewie-sequence.asm &&
gcc -mx32 -o stewie -Og -g golf-stewie-sequence.c golf-stewie-sequence.o
সাথে প্রথম পরীক্ষা-মামলা চালান ./stewie 8 1 3
যদি আপনার কাছে x32 লাইব্রেরি ইনস্টল করা না থাকে nasm -felf64তবে ডিফল্ট ব্যবহার করে জিসিসি ব্যবহার করুন এবং ছেড়ে দিন -m64। আমি আসলে (স্ট্যাকের) mallocপরিবর্তে float seqbuf[seqlen+8]এক্স 32 হিসাবে তৈরি না করে একটি কম ঠিকানা পেতে ব্যবহার করেছি।
মজাদার ঘটনা: YASM- এ একটি ত্রুটি রয়েছে: লুপ শাখার জন্য এটি একটি rel32 জিসিসি ব্যবহার করে, যখন শাখার লক্ষ্যবস্তুতে বৈশ্বিক প্রতীক হিসাবে একই ঠিকানা থাকে।
global stewie_x87_1reg
stewie_x87_1reg:
;; ended up moving all prologue code into the loop, so there's nothing here
.loop:
...
sub esi, 4
jg .loop
একত্রিত ... 11f: 0f 8f db ff ff ff jg 100 <stewie_x87_1reg>