x86-64 মেশিন কোড (লিনাক্স সিস্টেম কল): 78 বাইট
আরডিটিএসসি স্পিন-লুপের সময়, লিনাক্স sys_writeসিস্টেম কল।
x86-64 রান সময় আরডিটিএসসি "রেফারেন্স ক্লক" ফ্রিকোয়েন্সি জিজ্ঞাসা করার জন্য একটি সুবিধাজনক উপায় সরবরাহ করে না। আপনি একটি এমএসআর পড়তে পারেন (এবং তার উপর ভিত্তি করে একটি গণনা করুন) , তবে এর জন্য কার্নেল মোড, বা রুট + খোলার প্রয়োজন /dev/cpu/%d/msr, তাই আমি ফ্রিকোয়েন্সিটিকে বিল্ড-টাইম স্থির করার সিদ্ধান্ত নিয়েছি। ( FREQ_RDTSCপ্রয়োজনীয় হিসাবে সামঞ্জস্য করুন : কোনও 32-বিট ধ্রুবক মেশিন কোডের আকার পরিবর্তন করবে না)
নোট করুন যে বেশ কয়েক বছর ধরে x86 সিপিইউতে আরডিটিএসসি ফ্রিকোয়েন্সি ঠিক করা হয়েছে তাই এটি টাইমসোর্স হিসাবে ব্যবহারযোগ্য, না মূল ঘড়ি চক্র পারফরম্যান্স কাউন্টার, যদি না আপনি অক্ষম ফ্রিকোয়েন্সি পরিবর্তন করার জন্য পদক্ষেপ গ্রহণ করা। (আসল সিপিইউ চক্র গণনা করার জন্য সত্যিকারের পারফ কাউন্টার রয়েছে)) সাধারণত এটি টার্বো বা পাওয়ার সাশ্রয় ছাড়াই নামমাত্র স্টিকার ফ্রিকোয়েন্সি, যেমন আমার i7-6700k এর জন্য 4.0GHz for যাইহোক, এই ব্যস্ত-অপেক্ষা করার সময়টি লোড গড়ের উপর নির্ভর করে না (ক্যালিব্রেটেড বিলম্ব-লুপের মতো), এবং সিপিইউ পাওয়ার সাশ্রয়ের জন্যও সংবেদনশীল নয়।
এই কোডটি 2 ^ 32 Hz এর নিচে রেফারেন্স ফ্রিকোয়েন্সি সহ যেকোনো x86 এর জন্য কাজ করবে, অর্থাৎ ~ 4.29 গিগাহার্টজ পর্যন্ত। এর বাইরেও, টাইমস্ট্যাম্পের নীচের 32 টি 1 সেকেন্ডে সমস্তভাবে মুড়ে ফেলবে, সুতরাং আমাকে edxফলাফলের উচ্চ 32 বিটগুলিও দেখতে হবে।
সংক্ষিপ্তসার :
00:00:00\nস্ট্যাকের উপর ধাক্কা । তারপরে একটি লুপে:
sys_write সিস্টেম কল
- সময় বাড়ানোর জন্য অঙ্কগুলি (শেষের সাথে শুরু করে) এডিসি-লুপটি ১ দ্বারা বাড়ানো হবে
cmp / cmov, সিএফ প্রদানের ফলাফল নিয়ে বহন-ইন পরবর্তী অঙ্ক জন্য।
rdtsc এবং শুরু সময় সংরক্ষণ করুন।
- ঘুরাও
rdtscআরডিটিএসসি ফ্রিকোয়েন্সিটির ডেল্টা> = প্রতি সেকেন্ডে টিক্স না হওয়া পর্যন্ত চালান।
এনএএসএম তালিকা:
1 Address ; mov %1, %2 ; use this macro to copy 64-bit registers in 2 bytes (no REX prefix)
2 Machine code %macro MOVE 2
3 bytes push %2
4 pop %1
5 %endmacro
6
7 ; frequency as a build-time constant because there's no easy way detect it without root + system calls, or kernel mode.
8 FREQ_RDTSC equ 4000000000
9 global _start
10 _start:
11 00000000 6A0A push 0xa ; newline
12 00000002 48BB30303A30303A3030 mov rbx, "00:00:00"
13 0000000C 53 push rbx
14 ; rsp points to `00:00:00\n`
20
21 ; rbp = 0 (Linux process startup. push imm8 / pop is as short as LEA for small constants)
22 ; low byte of rbx = '0'
23 .print:
24 ; edx potentially holds garbage (from rdtsc)
25
26 0000000D 8D4501 lea eax, [rbp+1] ; __NR_write = 1
27 00000010 89C7 mov edi, eax ; fd = 1 = stdout
28 MOVE rsi, rsp
28 00000012 54 <1> push %2
28 00000013 5E <1> pop %1
29 00000014 8D5008 lea edx, [rax-1 + 9] ; len = 9 bytes.
30 00000017 0F05 syscall ; sys_write(1, buf, 9)
31
32 ;; increment counter string: least-significant digits are at high addresses (in printing order)
33 00000019 FD std ; so loop backwards from the end, wrapping each digit manually
34 0000001A 488D7E07 lea rdi, [rsi+7]
35 MOVE rsi, rdi
35 0000001E 57 <1> push %2
35 0000001F 5E <1> pop %1
36
37 ;; edx=9 from the system call
38 00000020 83C2FA add edx, -9 + 3 ; edx=3 and set CF (so the low digit of seconds will be incremented by the carry-in)
39 ;stc
40 .string_increment_60: ; do {
41 00000023 66B93902 mov cx, 0x0200 + '9' ; saves 1 byte vs. ecx.
42 ; cl = '9' = wrap limit for manual carry of low digit. ch = 2 = digit counter
43 .digitpair:
44 00000027 AC lodsb
45 00000028 1400 adc al, 0 ; carry-in = cmp from previous iteration; other instructions preserve CF
46 0000002A 38C1 cmp cl, al ; manual carry-out + wrapping at '9' or '5'
47 0000002C 0F42C3 cmovc eax, ebx ; bl = '0'. 1B shorter than JNC over a MOV al, '0'
48 0000002F AA stosb
49
50 00000030 8D49FC lea ecx, [rcx-4] ; '9' -> '5' for the tens digit, so we wrap at 59
51 00000033 FECD dec ch
52 00000035 75F0 jnz .digitpair
53 ; hours wrap from 59 to 00, so the max count is 59:59:59
54
55 00000037 AC lodsb ; skip the ":" separator
56 00000038 AA stosb ; and increment rdi by storing the byte back again. scasb would clobber CF
57
58 00000039 FFCA dec edx
59 0000003B 75E6 jnz .string_increment_60
60
61 ; busy-wait for 1 second. Note that time spent printing isn't counted, so error accumulates with a bias in one direction
62 0000003D 0F31 rdtsc ; looking only at the 32-bit low halves works as long as RDTSC freq < 2^32 = ~4.29GHz
63 0000003F 89C1 mov ecx, eax ; ecx = start
64 .spinwait:
65 ; pause
66 00000041 0F31 rdtsc ; edx:eax = reference cycles since boot
67 00000043 29C8 sub eax, ecx ; delta = now - start. This may wrap, but now we have the delta ready for a normal compare
68 00000045 3D00286BEE cmp eax, FREQ_RDTSC ; } while(delta < counts_per_second)
69 ; cmp eax, 40 ; fast count to test printing
70 0000004A 72F5 jb .spinwait
71
72 0000004C EBBF jmp .print
next address = 0x4E = size = 78 bytes.
pauseউল্লেখযোগ্য শক্তি সাশ্রয় করার নির্দেশকে অবিরাম মন্তব্য করুন : এটি এক কোরকে 15 ডিগ্রি সেলসিয়াস ছাড়াই উত্তপ্ত করে pause, তবে কেবল ~ 9 দিয়ে pause। (স্কাইলেকে, যেখানে pause~ 5 এর পরিবর্তে 100 ডলার চক্রের জন্য ঘুমায় I আমি মনে করি এটি যদি আরও সঞ্চয় করে তবেrdtsc আরও স্লো-ইশ না হয় তবে সিপিইউ খুব সময় না )।
একটি 32-বিট সংস্করণটি কয়েকটি বাইট সংক্ষিপ্ত হবে, উদাহরণস্বরূপ প্রাথমিক 00: 00: 00 00 n স্ট্রিংটি পুশ করতে এটির 32-বিট সংস্করণ ব্যবহার করা।
16 ; mov ebx, "00:0"
17 ; push rbx
18 ; bswap ebx
19 ; mov dword [rsp+4], ebx ; in 32-bit mode, mov-imm / push / bswap / push would be 9 bytes vs. 11
এবং 1-বাইট ব্যবহার করে dec edx। int 0x80সিস্টেম কল ABI- র ব্যবহার করেন না Esi / EDI, তাই প্রাপ্ত syscall বনাম lodsb / stosb জন্য রেজিস্টার সেটআপ সহজ হতে পারে।