Реализуйте простой секундомер


25

Вызов

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

правила

  • Время должно быть напечатано в hh:mm:ssформате. (начальные нули для однозначных значений)
  • Метки времени должны быть разделены CR, LF или CRLF. (без пробелов)
  • Новое время должно появляться каждую секунду. (стандартный вывод не может быть буферизован на секунду)
  • Поведение программы, если она запускается после 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);
    }
}

Критерии победы

Это , самый короткий код в байтах выигрывает!


Обратите внимание на дальнейшие проблемы, разъяснения в комментариях - плохая вещь. ссылка
user202729

Ответы:


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 обвиняет кеширование
Mr. 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»
Стэн Струм

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, чтобы начать, и снова, чтобы остановить.)

⎕AICcount Я нформация (идентификатор пользователя, вычислить время, время соединения, время манипуляция)

s← назначить s(для с терпким времени)
⎕AI-s вычитают sиз⎕AI

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

⎕DL 1Д е л ау один второй

→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

Баш + сон + дата, также 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

Swift , 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 Ой, исправлено!
Даррильо

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? Кроме того, входные данные datestr86400
Тьяго Олейник,

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

Попробуйте онлайн!

При разрешенном sleep (1) простые вложенные циклы for короче, чем при использовании встроенных методов обработки времени 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.
Эрик Outgolfer

@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в момент запуска.
user202729

Починил это. Спасибо
Noskcaj


2

Bash + coreutils + GNU date, 50 байтов

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

Вдохновленный @Dennis, это решение не требует изменения времени. Он хранит начальное смещение с настоящего времени до эпохи UNIX (1 января 1970 г. 00:00:00 UTC) в «o», а затем отображает [-ud опции] (текущее время - смещение) в дате UTC, но только [+% X опция] ЧЧ: ММ: СС. Это должно работать в странах, где текущий часовой пояс не является 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флаг, который считает пробелы и пропущенные инструкции как
Джо Кинг,

@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

Вот небольшой рисунок, чтобы увидеть, что он работает, как предполагалось, при использовании 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
Обычно вы должны дать людям шанс ответить на ваш вопрос, прежде чем отвечать на него самостоятельно. Я рекомендую неделю, так как некоторые могут быть здесь только в определенное время недели.
Адам

1
@ Adám Я не принял мой ответ, и в то время, когда я размещал намного более короткие ответы (например, ваши), они были отправлены.
MarkWeston

1

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

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
Добро пожаловать на сайт! Каждый ответ на вызов Code-Golf должен быть в гольфе. Вы должны по крайней мере удалить бесполезные пробелы и использовать 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 системный вызов
  • АЦП-цикл по цифрам (начиная с последней), чтобы увеличить время на 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 градусов C без 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системный вызов, но это было более интересно. С помощью root в 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.