Вкажіть простий секундомір


25

Виклик

Ваше завдання - написати програму, яка раз на секунду (включаючи негайно при запуску програми) друкує минулий час з моменту запуску програми.

Правила

  • Час повинен бути надрукований у hh:mm:ssформаті. (провідні нулі для одноцифрових значень)
  • Позначки часу повинні бути розділені CR, LF або CRLF. (немає провідних пробілів)
  • Новий час повинен з’являтися щосекунди. (stdout не можна буферувати ні секунди)
  • Поведінка програми, якщо вона виконується минулого 23:59:59, не визначена.
  • Ви можете використовувати, sleep(1)навіть якщо певна секунда може бути пропущена кожного разу, коли накладні для друку, обчислення, циклу тощо скупчуються до секунди.

Приклад виводу:

00:00:00
00:00:01
00:00:02
00:00:04
00:00:05
⋮

Зауважте, що 00:00:03тут відсутня через обробку накладних витрат. Фактичні пропущені значення (якщо такі є), звичайно, залежать від впровадження та / або системи.

Реалізація посилань на C: (лише сумісні з POSIX)

#include <unistd.h> // sleep()
#include <tgmath.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#ifndef __STDC_IEC_559__
#error "unsupported double"
#endif
static_assert(sizeof(double) == 8, "double must have double precision");
#define MAX_PRECISE_DOUBLE ((double)(1ULL << 52))

int main(void) {
    time_t start = time(NULL);
    if (start == (time_t)-1) return EXIT_FAILURE;
    while (1) {
        time_t now = time(NULL);
        if (now == (time_t)-1) return EXIT_FAILURE;

        double diff = difftime(now, start);
        if (isnan(diff) || diff < 0) return EXIT_FAILURE;
        if (diff > MAX_PRECISE_DOUBLE) return EXIT_FAILURE;

        unsigned long long seconds = diff;
        unsigned long long h = seconds / 3600;
        seconds %= 3600;
        unsigned long long m = seconds / 60;
        seconds %= 60;
        unsigned long long s = seconds;

        (void)printf("\r%02llu:%02llu:%02llu", h, m, s);
        (void)fflush(stdout);

        (void)sleep(1);
    }
}

Критерії виграшу

Це , найкоротший код у виграші байтів!


Зверніть увагу на наступні виклики, уточнення в коментарях - це погано. посилання
користувач202729

Відповіді:


9

MATL , 17 16 байт

`Z`12L/13XOD1Y.T

Спробуйте в MATL Online!

Як це працює

`         % Do...while loop
  Z`      %   Push seconds elapsed since start of program
  12L     %   Push 86400 (predefined literal)
  /       %   Divide. This transforms seconds into days
  13XO    %   Convert to date string with format 13, which is 'HH:MM:SS'
  D       %   Display
  1Y.     %   Pause for 1 second
  T       %   True. Used as loop condition for infinite loop
          % End loop (implicit)

4
Як у світі ви відповіли на це 37 хвилин після її закриття? o_O звинувачує кешування
Містер Xcoder

9
@ Mr.Xcoder Нещодавно я навчився використовувати силу
Луїс Мендо

29

Операція Мова сценаріїв Flashpoint ,  174  171 байт

s=""
#l
t=_time
t=t-t%1
a=t%60
c=(t-a)/60
b=c%60
c=(c-b)/60
d=""
e=d
f=d
?a<10:d=0
?b<10:e=0
?c<10:f=0
s=s+format["%1%2:%3%4:%5%6\n",f,c,e,b,d,a]
hint s
@t+1<_time
goto"l"

Дія:

158 байт, якщо попередній час буде перезаписано наступним разом:

#l
t=_time
t=t-t%1
a=t%60
c=(t-a)/60
b=c%60
c=(c-b)/60
d=""
e=d
f=d
?a<10:d=0
?b<10:e=0
?c<10:f=0
hint format["%1%2:%3%4:%5%6",f,c,e,b,d,a]
@t+1<_time
goto"l"

Технічно жодне повернення вагона не використовується, тому я не впевнений, чи обмежує ця версія правила.


5
Я не сподівався на точку роботи спалаху.
Полідуки

10
@Polyducks ніхто не очікує, що спалах точки роботи
Pureferret


Оскільки в Unix, CR замінить рядок, я вважаю, що друга відповідь підтверджена "дозволені або CR, LF, або CRLF"
Stan Strum

1
@StanStrum Принаймні на моєму Ubuntu CRне буде замінено рядок. Справді, CRLF, LFCRі LFвсе семантично еквівалентні.

13

Bash + coreutils, 28 26 байт

date -s0|yes date +c%T|sh

Недруковані символи між +і %є ESC байт.

Це встановлює системний час на 00:00:00 і, таким чином, вимагає root права. Він також передбачає, що часовий пояс - UTC, і жоден інший процес не буде перешкоджати системному годиннику.

Кожен новий термін скидає термінал, перезаписуючи таким чином попередній.


Bash + coreutils, 38 29 байт

date -s0|yes date +%T|sh|uniq

Діють ті самі обмеження, що і раніше. Кожен новий термін відображається в новому рядку.


Оскільки це не змінює рахунок рахунку, я б відокремив перший dateвід решти гарним маленьким підводним рядком . Але це може бути дуже приємно для когось, хто зможе придумати щось на зразок вашого другого рішення> :-(
Аарон

date -s0друкує новий час до STDOUT; Я використовую трубу, щоб заглушити цей вихід.
Денніс

О так, дякую за пояснення!
Аарон

5

APL (Dyalog Unicode) , 51 байт

Повний орган програми.

s←⎕AI
1↓∊':'@1∘⍕¨100+30 60 60 1E33⊃⎕AI-s
DL 1
2

Спробуйте в Інтернеті! (Натисніть Ctrl + Enter, щоб почати, і знову, щоб зупинити.)

⎕AIІнформація про кількість я (ідентифікатор користувача, час обчислення, час підключення, час введення)

s← призначити s(за s tart час)
⎕AI-s відняти sз⎕AI

3⊃ виберіть третій елемент (підключіть час у мілісекундах)
0 60 60 1E3⊤перетворіть на цей змішаний радіус:
3↑ візьміть перші 3 (краплі мілісекунди) по
100+ сто доданих до кожного (до нульових майданчиків)
':'@1∘⍕¨ виправлення двокрапкою при першому символі рядкового подання кожного
ϵ nlist (сплющувати)
1↓ випадати першу двокрапку (і неявно друкувати в stdout)

⎕DL 1D e l ay одну секунду

→2 перейти до рядка номер два


5

R , 59 44 байт

Fу R за замовчуванням до FALSE, але це звичайна змінна і може бути переглянута. При використанні в арифметиці FALSEпримушується до 0. Просити F+1тому повертається 1. Ми призначаємо Fбути F+1, відформатувати його добре, друкувати та чекати одну секунду. Триває нескінченно.

repeat{print(hms::hms(F<-F+1))
Sys.sleep(1)}

Не працює в TIO (через відсутність hmsпакета), але ось зразок виводу з моєї машини:

00:00:00
00:00:01
00:00:02
00:00:03
00:00:04
00:00:05
00:00:06
00:00:07
00:00:08
00:00:09
00:00:10
00:00:11
00:00:12
00:00:13

5

bash + сон + дата, також 50 49 47 46 45 41 байт

while date -ud@$[s++] +%T;do sleep 1;done

Щоб зайняти час на коліні, швидко натисніть ^ C, запустіть його та повторіть вищезазначене:

laps=("${laps[@]}" $s) ; echo ${laps[-1]}

Щоб скинути:

s=0; unset laps

Синтаксис $ [s ++] все ще працює, але більше не є (AFAICS), задокументованим на довільній bashсторінці. І це все-таки байт коротший, ніж використання циклу for ((...)), як тільки я видалив лапки навколо нього.


AFAICT, $[]є застарілою / недокументованою, але все ще підтримується формою$(()) . Я не впевнений, чи зазвичай він використовується у відповідях на код-гольф, але загальне правило полягає в тому, що ваш код повинен працювати лише принаймні на одній версії інтерпретатора для вашої мови. ІМО це добре.
Пітер Кордес

s=0не потрібно, оскільки арифметична підміна буде вважати незміненою змінною як 0 . -uтакож не потрібен, якщо ви просто припускаєте часовий пояс за замовчуванням (UTC).
Денніс

-у потрібен на моїй машині :)
Буде Кроуфорд

4

Свіфт , 144 байти

import Foundation
let s=Date()
while 1>0{let d=Int(-s.timeIntervalSinceNow)
print(String(format:"%02d:%02d:%02d",d/3600,d/60%60,d%60))
sleep(1)}

Пояснення

import Foundation                       // Import `Date` and `sleep()`
let s = Date()                          // Get the time at the start of the program
while 1 > 0 {                           // While 1 > 0 (forever):
  let d = Int(-s.timeIntervalSinceNow)  //   Calculate time difference
  print(String(format:"%02d:%02d:%02d", //   Print the time
      d/3600,d/60%60,d%60))
  sleep(1)                              //   Sleep one second
}

4

JavaScript (ES6), 99 байт

f=_=>console.log(new Date(new Date-d).toUTCString().slice(17,25))
f(d=Date.now(setInterval(f,1e3)))


2
Години не починаються з 0 для мене. Зсув змінюється залежно від часового поясу системного годинника. (Win10)
LukeS

@LukeS Whoops, виправлено!
darrylyeo

4

Matlab (R2016b), 50 байт

t=now;while 1,disp(datestr(now-t,13)),pause(1),end

Пояснення:

t=now; % Stores the current time
while 1 % Loops forever
    disp(datestr(now-t,13)) % Computes the difference since the program started
    % And prints with format 13 ('HH:MM:SS') - this may change between versions
    pause(1) % Waits one second
end

Альтернативна версія (також 50 байт: P):

now;while 1,disp(datestr(now-ans,13)),pause(1),end

Ласкаво просимо на сайт! :)
DJMcMayhem

Спасибі товариш:)
Тіаго Олейнік

@LuisMendo Дякую за пропозицію, але я не зрозумів ... У вашому прикладі, що таке змінна t? Крім того, вхід datestrє в днях, тому мені доведеться ділити на 86400, що збільшить кількість байтів на два ...
Тіаго Олейник

3

Джулія 0,6 , 75 68 байт

for h=0:23,m=0:59,s=0:59;@printf "%02i:%02i:%02i
" h m s;sleep(1)end

Спробуйте в Інтернеті!

Якщо режим сну (1) дозволений, прості вкладені петлі коротші, ніж використання вбудованих методів обробки часу Julias.

Старе рішення без сну (1) за допомогою DateTime

t=now()-DateTime(0);Timer(x->println(Dates.format(now()-t,"HH:MM:SS")),0,1)

t- кількість часу, що пройшло від 'дня 0' до початку запуску програми. now()-t- це момент часу , який потім форматується за допомогою Dates.format().

t0=now(); ...; now()-t0дасть різницю у часі , з якою не можна користуватися Dates.format().

Сам термін тривіальний із вбудованим Timer.


3

Python 2 , 85 байт

import time
t=0
while 1:print(":%02d"*3)[1:]%(t/3600,t/60%60,t%60);time.sleep(1);t+=1

Кредити


Ви можете зберегти один байт, замінивши "%02d:%02d:%02d"на(":%02d"*3)[1:]
wnnmaw

1
Вам не потрібно %24, поведінка не визначена після 23:59:59.
Ерік Аутгольфер

@EriktheOutgolfer Хороший момент, оновлено.
Ніл

3

JavaScript (ES6), 88 байт

f=_=>console.log(new Date(i++*1e3).toUTCString().slice(17,25))
f(i=0,setInterval(f,1e3))

По суті той же підхід, що і і у відповіді @ darrylyeo , але працює для всіх часових поясів і використовує дещо інший спосіб дістатися до 0.

[Редагувати] Відповідь Дарріла виправлено. Це все ж коротше.


3

> <> , 82 + 7 = 89 байт

0\!
:/+1oan~?=3ln?$0(a:o":"n~?=4ln?$0(a:ro":"n~?=5ln?$0(a:,*a6-}:%*a6:,*a6-}:%*a6:

Спробуйте в Інтернеті!

+7 байт для використання прапора -t.0125 щоб кожна інструкція займала 1/80 частину секунди. Кожна петля має 80 інструкцій, роблячи кожну петлю однією секундою. Через час обчислень це практично більше.

Я на насправді був в буфер це все, аж до 100 , поки я не побачив @Not дерево в відповідь , який мав 7 байт краще, ніж мій , щоб генерувати годинник і хвилини, підрізування нижче 80. Крім того, вони натхненні, використання \/яких виконуються два рази на петлю.

Як це працює

0\...
./...
Initialises the stack with a 0 to represent the time

0\!
:/....................................................,*a6-}:%*a6:,*a6-}:%*a6:
Puts the hours, minutes and seconds in the stack

0\!
:/....n~?=3ln?$0(a:o":"n~?=4ln?$0(a:ro":"n~?=5ln?$0(a:...
Print out the hours, minutes, seconds separated by colons. 
If the number is below 0, print a leading 0. 
If the number is not, then there is an extra 0 on the stack, which is popped.

0\!
./+1oa...
Print a newline and increment the counter
And restart the loop

Бонус:

Однорядкова версія одного розміру, 80 + 9 байт:

0::6a*%:}-6a*,:6a*%:}-6a*,:a(0$?nl5=?~n":"or:a(0$?nl4=?~n":"o:a(0$?nl3=?~nao1+>!

При цьому використовується -aпрапор, щоб додати галочки для пропущених інструкцій.


3

PHP 4+, 70 64 байти

$x=time();while(1){sleep(1);echo date('H:i:s',time()-$x)."\n";}

PHP 5.3+, 69 63 байти

$x=time();a:sleep(1);echo date('H:i:s',time()-$x)."\n";goto a;

Відкриті теги PHP можуть бути пропущені у відповіді, заощадивши 6 байтів.
Даніель В.

2

Python 3 , 112 байт

Якщо припустити, що використання затримки на 1 секунду нормально, навіть якщо вона (рідко) може пропустити секунду.

from time import*;a=0
while 1:d=divmod;m,s=d(a,60);print(":".join(f"{k:02d}"for k in(*d(m,60),s)));a+=1;sleep(1)

2

VBA, 90

t=0:while(1):?format(t,"hh:mm:ss"):t=t+timeserial(0,0,1):q=timer:while q-timer<1:wend:wend

запустіть у прямому вікні: очікувана точка відмови десь близько 23 мільйонів років (дозвіл плаваючої точки не вдається ~ 8.5e9 днів)



2

AWK , 110 87 86 байт

BEGIN{for(;;i++){printf("%02d:%02d:%02d\n",i/3600%60,i/60%60,i%60);system("sleep 1")}}

Не працює в TIO.


Здається, ваша програма не друкується 00:00:00в момент запуску.
користувач202729

Виправлено це. Спасибі
Noskcaj


2

Баш + coreutils + дата GNU, 50 байт

o=`date +"%s"`;yes date +%X -ud\"-$o sec\"|sh|uniq

Це рішення, натхнене @Dennis, не потребує часу для зміни. Він зберігає початковий зсув відтепер до епохи UNIX (1 січня 1970 00:00:00 UTC) у "o", а потім відображає [-ud options] (поточний час - зміщення), у даті UTC, але лише [+% X варіант] HH: MM: SS. Це має працювати в країнах, де поточний часовий пояс не є UTC.


2

Чистота , 173 172 168 байт

import StdEnv,System.Time
$n i#i=(i/60^n)rem 60
=(i/10,i rem 10)
f i w#(Clock j,w)=clock w
#j=j/1000
|j>i=[j:f j w]=f i w
Start w=[($2i,':',$1i,':',$0i,'
')\\i<-f -1 w]

Цей працює лише під пакетами Windows Clean.

Додайте 3 байти, якщо ви хочете, щоб він працював під Linux, як Clean CLK_PER_TICK :== 1000000на * nix. Якщо ви хочете, щоб він був кросплатформенним, додайте замість нього 8 байт, як вам потрібно використовувати CLK_PER_TICKзамість значення, яке воно встановлено. ( Посилання TIO більше за вище )

Спробуйте в Інтернеті!


2

Python 2 , 69 + 3 ( TZ=) = 72 байти

from time import*;s=time()
while 1:print ctime(time()-s)[11:19]+'\r',

Це працює в безперервному циклі, без сну, не оновлюючи час на тій же лінії, а не друкуючи новий рядок щосекунди. (Я все-таки дозволений правилами, сподіваюся.)

Ця трохи довша версія (72 + 3 = 75 байт) друкує нову лінію щосекунди замість цього:

from time import*;s=time()
while 1:print ctime(time()-s)[11:19];sleep(1)

Обидва з них вимагають, щоб Ви знаходитесь у часовому поясі UTC. У Linux ви можете досягти цього, встановившиTZ змінну середовища. Напр TZ= python.


2

> <> , 106 байт 82 + 9 = 91 байт

Дякую Джо Кінгу за пропозицію -aпрапора! Перевірте їх відповідь .

0v+1oan<n0/
:/<</?(a:,*a6-}:%*a6:,*a6-}:%*a6:\\
n<n0/<</?(a:ro":"
":"n<n0/<</?(a:o

Спробуйте в Інтернеті! (але вам доведеться почекати 60-секундного тайм-ауту).

Мені потрібно скористатися функцією> <>, яка мені ніколи раніше не потрібна: для цього коду потрібен прапор -t.0125 , який встановлює швидкість виконання 0,0125 секунди на галочку, або 80 тиків на секунду. Там також є-a прапор, завдяки якому пробіли вважаються галочкою (в деяких випадках - перекладач з цього приводу трохи дивний).

В основному, код зберігає лічильник, який збільшується кожного разу, коли риба проходить цикл, а решта циклу перетворює лічильник на hh:mm:ss формат і друкує його. Петля бере рівно 80 кліщів.

Це повинно працювати теоретично, але на практиці кожен тик трохи довше 0,0125 секунд через час обчислення. Зміна\\ другого рядка на<< точніші терміни на TIO.

Ви також можете подивитися код у дії на рибному майданчику , за винятком того, що цей перекладач трактує пробіли дещо інакше, ніж офіційний перекладач. Крім того, ви можете видалити прапори на TIO, щоб змусити код працювати з максимальною швидкістю, щоб перевірити поведінку протягом декількох хвилин.


-1 байт, замінивши v перший рядок на \!і видаливши два зайвих <. Ще пара байтів, якщо ви використовуєте -aпрапор, який рахує пробіл та пропущені інструкції як кліщі
Jo King

@JoKing, -aПрапор дозволив мені трохи більше гольфу, дякую! Я думаю, ви також можете використовувати \!трюк у своєму коді: Спробуйте в Інтернеті!
Не дерево

2

Java 8, повна програма, 150 байт

interface M{static void main(String[]a)throws Exception{for(int i=0;;Thread.sleep(1000))System.out.printf("%02d:%02d:%02d%n",i/3600,i/60%60,i++%60);}}

Спробуйте його тут (час вийшов через 60 секунд, тому я встановив сон на 1, щоб побачити більше результатів).

Пояснення:

interface M{                    // Program:
  static void main(String[]a)   //  Mandatory main-method
     throws Exception{          //    Mandatory throws for Thread.sleep
    for(int i=0;                //   Start at 0
        ;                       //   Loop indefinitely
         Thread.sleep(1000))    //     After every iteration: Sleep for 1 sec
      System.out.printf("%02d:%02d:%02d%n",
                                //    Print in the format "HH:mm:ss\n":
        i/3600,i/60%60,i++%60); //     The hours, minutes and seconds
                                //     (and increase `i` by 1 afterwards with `i++`)
                                //   End of loop (implicit / single-line body)
  }                             //  End of mandatory main-method
}                               // End of program

Java 8, функція, 94 байти

v->{for(int i=0;;Thread.sleep(1000))System.out.printf("%02d:%02d:%02d%n",i/3600,i/60%60,i++%60);}

Спробуйте його тут (час вийшов через 60 секунд, тому я встановив сон на 1, щоб побачити більше результатів).

Пояснення:

v->{   // Method with empty unused parameter and no return-type
  ...  //  Same as the program above
}      // End of method

Ось невеликий gif, щоб побачити, як він працює за призначенням, коли використовується 1000 мс:

введіть тут опис зображення


2

PHP, 59 48 байт

while(1){sleep(1);echo date('H:i:s',$i++)."\n";}

Натхненний відповідь Даррен Ейч .

Стара версія :

<?php while(1){sleep(1);echo date('H:i:s',$i++-3600)."\n";}

Відкриті теги PHP можуть бути пропущені у відповіді, заощадивши 6 байтів.
Даніель В.

Чудова думка, але 3600 має бути 86400, інакше лічильник починається о 23:00:00, тож, на жаль, ти набираєш байт, все-таки б'єш мене на 9, хоча!
Даррен Х

@DarrenH Я думаю, це залежить від вашої мови, я не думав про це. Я в GMT + 1, тому я додав 3600, але, мабуть, для англійців, ви можете вилучити -3600взагалі, що дозволить заощадити 5 байт.
roberto06

1

Оболонка , 177 байт

Зауважте, що це не повністю POSIX-сумісний, оскільки він використовується date +%s, що є загальним dateрозширенням.

a=`date +%s`;while true;do b=`date +%s`;s=`expr $b - $a`;h=`expr $s / 3600`;s=`expr $s % 3600`;m=`expr $s / 60`;s=`expr $s % 60`;printf '\r%02d:%02d:%02d' $h $m $s;sleep 1;done

7
Зазвичай ви повинні дати людям можливість відповісти на ваше завдання, перш ніж відповісти на нього самостійно. Я рекомендую тиждень, оскільки деякі можуть бути тут лише в певний час протягом тижня.
Adám

1
@ Adám, я не прийняв свою відповідь, і тоді я надсилав набагато більш короткі відповіді (як і ваша).
MarkWeston

1

Рубі, 192 117 байт (Кредит Dada)

t=Time.now
loop do
m,s=(Time.now-t).to_i.divmod(60)
h,m=m.divmod(60)
printf"%02d:%02d:%02d
",h,m,s
sleep 1
end

Як це працює?

Перейдемо до використання розгорнутої версії (Перетворення на час задається як окрема функція та використовує інший вихідний формат):

def format_secs(s) # Converts the value in seconds to the required format
    mins, secs = s.divmod(60) # divmod returns the quotient and the remainder of a number
    hours, mins = mins.divmod(60)
    [hours,mins,secs].map { |e| e.to_s.rjust(2,'0') }.join ':'

    =begin
    [hours,mins,secs] -Creates a new array using the values allready provided for hours, minutes and seconds
    .map { - Creates a new array based on a operation on each of an array's values
    .to_s.rjust(2,'0')} - Turns the number into a string, and then adds "0" if needed to make the timer's result at least two digits
    .join ':' - Combines the result of the operation into a single string with a ":" in between the two numbers
    =end
end

t = Time.now # Saves the time at the program's (Rough) start

loop do
    puts format_secs((Time.now - t).to_i) # Returns the result of  the "format_secs" operation on the difference between the two times (in seconds) converted to a pure integer
    sleep 1 # Waits for one second
end

6
Ласкаво просимо на сайт! Кожна відповідь на проблему з кодовим гольфом повинна бути гольф. Вам слід принаймні видалити непотрібні пробіли та використовувати 1-символьні назви змінних. Це дозволить отримати близько 120 байтів, а використання, printfзамість цього, putsможе заощадити ще кілька байт: Спробуйте це в Інтернеті! . Щасливого гольфу на PPCG!
Дада

1

APL NARS, 109 63 57 символів

q;t
t←0
{∊⍵,¨':: '}{1<⍴x←⍕⍵:x⋄'0',x}¨(3⍴60)⊤⌊t+←⎕DL 1⋄→2

3 + 3 + 48 + 3 = 57 (бачимо й інші рішення Apl)

{1<⍴x←⍕⍵:x⋄'0',x}

перетворимо INT ⍵ у рядок цифр таким чином, якщо довжина цього рядка дорівнює 1, ніж додати один "0" перед ним

{∊⍵,¨':: '}

об'єднати масив у ⍵ з масивом '::'

00:00:01 
00:00:02 
00:00:03 
00:00:04 
00:00:05 
00:00:06 
00:00:07 
00:00:08 
00:00:09 

1

машинний код x86-64 (виклик системи Linux): 78 байт

RDTSC синхронізування синхронізування, sys_writeсистемний виклик Linux .

x86-64 не забезпечує зручний спосіб запиту частоти RDTSC "опорний такт" під час виконання. Ви можете прочитати MSR (і зробити розрахунок, виходячи з цього) , але для цього потрібен режим ядра або root + відкриття /dev/cpu/%d/msr, тому я вирішив зробити частоту постійною тривалістю збірки. (За FREQ_RDTSCнеобхідності відрегулюйте : будь-яка 32-бітова константа не змінить розмір машинного коду)

Зауважте, що процесори x86 протягом декількох років мали фіксовану частоту RDTSC, тому вона може використовуватися як часовий ресурс, а не основний лічильник продуктивності циклу тактових частот, якщо не вжити заходів для відключення зміни частоти. (Існують фактичні лічильники парфумів для підрахунку реальних циклів процесора.) Зазвичай він тикає на номінальній частоті наклейки, наприклад, 4,0 ГГц для мого i7-6700k незалежно від турбо або енергозбереження. У будь-якому випадку, цей час зайнятості очікування не залежить від середнього навантаження (як би калібрований цикл затримки), а також не чутливий до енергозбереження процесора.

Цей код буде працювати для будь-якого x86 з опорною частотою нижче 2 ^ 32 Гц, тобто до ~ 4,29 ГГц. Крім того, низький 32 часової позначки завершиться повністю за 1 секунду, тому мені доведеться також подивитися на edxвисокі 32 біти результату.

Підсумок :

натисніть 00:00:00\nна стек. Потім у циклі:

  • sys_write системний виклик
  • Цикл ADC над цифрами (починаючи з останнього), щоб збільшити час на 1. Обертання / перенесення обробляється з cmp/ cmov, при цьому результат CF забезпечує перенесення наступної цифри.
  • rdtsc і заощадити час початку.
  • крутиться rdtscдо тих пір, поки дельта не стане> = тик за секунду частоти RDTSC.

Список NASM:

 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. (На Skylake, де pauseспиться ~ 100 циклів замість ~ 5. Я думаю, що це би заощадило більше, якби rdtscне було також повільним результатом, тому процесор не займає багато часу).


32-розрядна версія буде на кілька байтів коротшою, наприклад, використовуючи 32-бітну версію цієї програми, щоб натиснути початковий \ 00: 00: 00 \ n рядок.

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 не використовуватиме єси / ЕОД, тому установка регістра для системних викликів проти LODSB / STOSB може бути простіше.


Я міг би використати nanosleepсистемний дзвінок, але це було цікавіше. З коренем на Linux, можна прочитати правильний MSR і програмно отримати частоту RDTSC.
Пітер Кордес

1

q / kdb + , 40 байт

Рішення:

.z.ts:{-1($)18h$a+:1};a:-1;(.)"\\t 1000"

Приклад:

q).z.ts:{-1($)18h$a+:1};a:-1;(.)"\\t 1000"
q)00:00:00
00:00:01
00:00:02
00:00:03
00:00:04
00:00:05

Пояснення:

Тут виконуються три команди:

  1. .z.ts:{-1($)18h$a+:1}; / override timer function
  2. a:-1; / initialise variable a to -1
  3. (.)"\\t 1000" / start the timer with 1000ms precision

Розбивка функції таймера:

.z.ts:{-1 string 18h$a+:1} / ungolfed timer function
      {                  } / lambda function
                     a+:1  / add 1 to variable a
                 18h$      / cast to seconds
          string           / cast to string
       -1                  / write to stdout
.z.ts:                     / assign this function to .z.ts

Бонус:

Альтернатива 1 на 41 байт :

a:.z.t;.z.ts:{-1($)18h$x-a};(.)"\\t 1000"

Альтернатива 2 на 26 + 7 байт = 33 байти

.z.ts:{-1($)18h$a+:1};a:-1

і додавання -t 1000в якості аргументів до двійкового файлу q.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.