1

Я уже жаловался, что в Ubuntu-14.04 в Qt таймер неточно отрабатывает
заданный интервал, вместо 4000ms проходит 4063.
Сегодня обнаружилась ещё более странная вещь уже в консоли в программе на C.
Это маленькая тестовая программа, там сначала создаётся udp-сокет, для него
запускается recvfrom и ждёт обращения, а когда оно приходит, то с помощью
sendto отсылает его обратно. Затем там в цикле передаётся некий массив, но
это несущественно. Оказалось, что, если после выполнения sendto вставить
вызов gettimeofday, всё равно, перед циклом или внутри, то sendto ничего
не пересылает и заканчивется с ошибкой 22 и возвращает -1.

Т.е. только присутствие в тексте программы gettimeofday приводит
к неработоспособности. В Astra Linux, основанной на Debian Wheezy,
и то и другое работает правильно.

Пётр.

Пётр.

1. думаю стоит установить lowlatency ядро, если Вы хотите точного тайминга.

name -a
Linux asm-B113 3.16.0-29-lowlatency #39-Ubuntu SMP PREEMPT Tue Dec 16 21:12:19 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

3

Peter, где код?

Peter пишет:

Я уже жаловался, что в Ubuntu

ССЗБ.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

4 (28.01.2015 14:01:08 отредактировано Peter)

Харитон пишет:

1. думаю стоит установить lowlatency ядро, если Вы хотите точного тайминга.

name -a
Linux asm-B113 3.16.0-29-lowlatency #39-Ubuntu SMP PREEMPT Tue Dec 16 21:12:19 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

Ядро скомпилировано с LOWLATENCY и HZ=1000.

Пётр.

drBatty пишет:

Peter, где код?

Peter пишет:

Я уже жаловался, что в Ubuntu

ССЗБ.

Приведу попозже, но там именно то, что я описал: помещение gettimeofday() после sendto()
каким-то образом на неё влияет.

Пётр.

Пётр.

5

Peter пишет:

Приведу попозже, но там именно то, что я описал: помещение gettimeofday() после sendto()
каким-то образом на неё влияет.

вангую, что вы как-то не правильно делаете, и добились UB.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

6 (28.01.2015 17:01:22 отредактировано Peter)

drBatty пишет:
Peter пишет:

Приведу попозже, но там именно то, что я описал: помещение gettimeofday() после sendto()
каким-то образом на неё влияет.

вангую, что вы как-то не правильно делаете, и добились UB.

Однако в Astra Linux (Debian Wheezy), эта программа работает. Вот её код с отступами:

#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <bool.h>
//#include <cam_udp.h>
typedef union uns_short_int {
    unsigned short int us;
    unsigned char ch[2];
} uns_short_int;
int main() {
    struct timeval tv1, tv2;
    unsigned long long int nt1;
    uns_short_int usi;
    socklen_t lenf;
    int i, j, m1, n, ncyc, nfdc, nj, nfds, nfd, nr, len, lencp, lenj;
    bool b;
    char buf[192];
    unsigned char bufr[1024];
    unsigned short int uvp = 50616, ucp = 50700;    //ucp=COMMAND_PORT;
    unsigned int usc = 62;
    struct sockaddr_in addrcs, addrcc, addrvs;
    FILE *fin, *fout;
  llen:len = sizeof(struct sockaddr_in);
    if (len == 0)
    goto llen;
    fin = fopen("imit_cam_cyc.in", "r");
    if (fin != NULL) {
    n = fscanf(fin, "%hu%[^\n]", &uvp, buf);
    n = fscanf(fin, "%u%[^\n]", &usc, buf);
    n = fscanf(fin, "%d%[^\n]", &ncyc, buf);
    n = fscanf(fin, "%s%[^\n]", buf, buf + 20);
    fclose(fin);
    } else {
    strcpy(buf, "127.0.0.1");
    ncyc = 0;
    }
    usi.us = 0;
    b = true;
    usc *= 1000;
    fout = fopen("imit_cam_cyc.out", "w");
    lencp = 32;            //lencp=sizeof(CONTROL_PACKET);
    fprintf(fout, "input: %d %d  %u %hu %d\n", len, lencp, usc, uvp, ncyc);
    fflush(fout);
    bzero((void *) &addrcs, len);
    bzero((void *) &addrcc, len);
    bzero((void *) &addrvs, len);
    addrcs.sin_addr.s_addr = INADDR_ANY;
    addrcs.sin_family = AF_INET;
    addrcs.sin_port = htons(ucp);
    nfdc = socket(AF_INET, SOCK_DGRAM, 0);
    n = bind(nfdc, (struct sockaddr *) &addrcs, len);
    if (n < 0) {
    strcpy(buf, strerror(errno));
    fprintf(fout, "bind: %d %s\n", errno, buf);
    }

    while (b) {
    n = recvfrom(nfdc, bufr, lencp, MSG_WAITALL,
             (struct sockaddr *) &addrcc, &lenf);
    if (n < 0) {
        strcpy(buf, strerror(errno));
        fprintf(fout, "recvrom: %d %s\n", errno, buf);
        fflush(fout);
        return 0;
    }
    if (n == lencp) {
        fprintf(fout, "request: %d %d\n", n, nfdc);
        fflush(fout);
        n = sendto(nfdc, bufr, lencp, MSG_DONTROUTE,
               (struct sockaddr *) &addrcc, len);
        fprintf(fout, "answer: %d\n", n);
        fflush(fout);
        if (n != lencp) {
        strcpy(buf, strerror(errno));
        fprintf(fout, "sendto: %d %s\n", errno, buf);
        fflush(fout);
        return 0;
        }
        b = 0;
    }
    }
    addrvs = addrcc;
    addrvs.sin_port = htons(uvp);
    nfds = socket(AF_INET, SOCK_DGRAM, 0);
    strcpy(buf + 20, "/home/operator1/work/jpg300/jpg_udp_000");
    nj = 20 + strlen(buf + 20);

    while (1) {
    for (j = 0; j < ncyc; j++) {
        sprintf(buf + nj - 3, "%03d", j);
        nfd = open(buf + 20, O_RDONLY);
        if (nfd < 0) {
        strcpy(buf, strerror(errno));
        fprintf(fout, "open: %d %s\n", errno, buf);
        fflush(fout);
        return 0;
        }
        lenj = lseek(nfd, 0, SEEK_END);
        n = lseek(nfd, 0, SEEK_SET);
        nr = lenj / 1024;
        if (nr * 1024 != lenj) {
        fprintf(fout, "lenj: %d %d %s\n", j, lenj, buf + 20);
        fflush(fout);
        }
        gettimeofday(&tv1, NULL);
        for (i = 0; i < nr; i++) {
        n = read(nfd, bufr, 1024);
        bufr[2] = usi.ch[1];
        bufr[3] = usi.ch[0];
        n = sendto(nfds, bufr, 1024, MSG_DONTROUTE,
               (struct sockaddr *) &addrvs, len);
        if (n < 0) {
            strcpy(buf, strerror(errno));
            fprintf(fout, "sendto: %d %d %s\n", i, errno, buf);
            fflush(fout);
        }
        usi.us++;
        }
        close(nfd);
        gettimeofday(&tv2, NULL);
        nt1 =
        (tv2.tv_sec - tv1.tv_sec) * 1000000 + tv2.tv_usec - tv1.tv_usec;
        m1 = nt1;
        n = usleep(usc - m1);
        if (n < 0) {
        strcpy(buf, strerror(errno));
        fprintf(fout, "usleep: %d %d %s\n", i, errno, buf);
        fflush(fout);
        }
    }
    }
    fclose(fout);
    return 0;
}

А вот её код в удобном для меня виде:

#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <bool.h>
//#include <cam_udp.h>
typedef union uns_short_int{
  unsigned short int us;
  unsigned char ch[2];
}uns_short_int;
int main() {
struct timeval tv1,tv2;
unsigned long long int nt1;
uns_short_int usi;
socklen_t lenf;
int i,j,m1,n,ncyc,nfdc,nj,nfds,nfd,nr,len,lencp,lenj;
bool b;
char buf[192];
unsigned char bufr[1024];
unsigned short int uvp=50616,ucp=50700;    //ucp=COMMAND_PORT;
unsigned int usc=62;
struct sockaddr_in addrcs,addrcc,addrvs;
FILE *fin,*fout;
llen:  len=sizeof(struct sockaddr_in);if(len==0)goto llen;
 fin=fopen("imit_cam_cyc.in","r");if(fin!=NULL){n=fscanf(fin,"%hu%[^\n]",&uvp,
  buf); n=fscanf(fin,"%u%[^\n]",&usc,buf); n=fscanf(fin,"%d%[^\n]",&ncyc,buf);
  n=fscanf(fin,"%s%[^\n]",buf,buf+20); fclose(fin);}
  else{strcpy(buf,"127.0.0.1");ncyc=0;}usi.us=0;b=true;usc*=1000;
 fout=fopen("imit_cam_cyc.out","w"); lencp=32;    //lencp=sizeof(CONTROL_PACKET);
fprintf(fout,"input: %d %d  %u %hu %d\n",len,lencp,usc,uvp,ncyc);fflush(fout);
 bzero((void*)&addrcs,len);bzero((void*)&addrcc,len);bzero((void*)&addrvs,len);
 addrcs.sin_addr.s_addr=INADDR_ANY;addrcs.sin_family=AF_INET;
 addrcs.sin_port=htons(ucp);nfdc=socket(AF_INET,SOCK_DGRAM,0);
 n=bind(nfdc,(struct sockaddr*)&addrcs,len);
 if(n<0){strcpy(buf,strerror(errno));fprintf(fout,"bind: %d %s\n",errno,buf);}

 while(b){n=recvfrom(nfdc,bufr,lencp,MSG_WAITALL,(struct sockaddr*)&addrcc,&lenf
 );if(n<0){strcpy(buf,strerror(errno));fprintf(fout,"recvrom: %d %s\n",errno,
  buf);fflush(fout);return 0;}if(n==lencp){
fprintf(fout,"request: %d %d\n",n,nfdc);fflush(fout);
  n=sendto(nfdc,bufr,lencp,MSG_DONTROUTE,(struct sockaddr*)&addrcc,len);
fprintf(fout,"answer: %d\n",n);fflush(fout);
 if(n!=lencp){strcpy(buf,strerror(errno));fprintf(fout,"sendto: %d %s\n",
 errno,buf);fflush(fout);return 0;}b=0;}}
 addrvs=addrcc;addrvs.sin_port=htons(uvp); nfds=socket(AF_INET,SOCK_DGRAM,0);
 strcpy(buf+20,"/home/operator1/work/jpg300/jpg_udp_000");nj=20+strlen(buf+20);

 while(1){for(j=0;j<ncyc;j++){sprintf(buf+nj-3,"%03d",j);
 nfd=open(buf+20,O_RDONLY);
 if(nfd<0){strcpy(buf,strerror(errno));fprintf(fout,"open: %d %s\n",errno,buf);
 fflush(fout);return 0;}
 lenj=lseek(nfd,0,SEEK_END);n=lseek(nfd,0,SEEK_SET);nr=lenj/1024;
 if(nr*1024!=lenj){fprintf(fout,"lenj: %d %d %s\n",j,lenj,buf+20);fflush(fout);}
 gettimeofday(&tv1,NULL);for(i=0;i<nr;i++){n=read(nfd,bufr,1024);
 bufr[2]=usi.ch[1];bufr[3]=usi.ch[0];
 n=sendto(nfds,bufr,1024,MSG_DONTROUTE,(struct sockaddr*)&addrvs,len);
 if(n<0){strcpy(buf,strerror(errno));fprintf(fout,"sendto: %d %d %s\n",i,
 errno,buf);fflush(fout);}usi.us++;}close(nfd);gettimeofday(&tv2,NULL);
 nt1=(tv2.tv_sec-tv1.tv_sec)*1000000+tv2.tv_usec-tv1.tv_usec;m1=nt1;
 n=usleep(usc-m1);if(n<0){strcpy(buf,strerror(errno));fprintf(fout,
 "usleep: %d %d %s\n",i,errno,buf);fflush(fout);}}}fclose(fout);
  return 0;
}

Там включается bool.h, это

#ifndef BOOL_H
#define BOOL_H 1

typedef int bool;
#define true 1
#define false 0

#endif

Если gettimeofday() поместить, например, перед 2-м while(), то sendto() возвращает -1 и заканчивается с ошибкой 22.
То же самое в варианте программы без цикла после sendto(), вернее, когда в цикле всё время передаётся один и тот же файл.
jpg_udp_nnn - это файлы, состоящие из udp-пакетов для очередного jpg, но это не важно, важно, что gettimeofday()
влияет на предидущий вызов sendto().

Пётр.

Пётр.

7

ошибка 22 - это EINVAL. Ищите у себя: либо что-то неправильно вызываете, либо нагадили в память (переполнился какой-нибудь буфер)
Я в вашем "кирпиче" ничего не нашёл, но особо внимательно и не проверял.

8

Peter пишет:

FILE *fin, *fout; llen:len = sizeof(struct sockaddr_in); if (len == 0) goto llen;

досвидания. Увидимся в следующей жизни, когда вы научитесь писать.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

9

drBatty пишет:
Peter пишет:

FILE *fin, *fout; llen:len = sizeof(struct sockaddr_in); if (len == 0) goto llen;

досвидания. Увидимся в следующей жизни, когда вы научитесь писать.

Это игра ума. Ещё раньше я замечал, что для struct sockaddr_in sizeof() часто возвращает
0 или -1, уже не помню. И зачем я буду себя ограничивать в коде?

Пётр.

Пётр.

10

Peter пишет:

И зачем я буду себя ограничивать в коде?

не, не нужно. Наоборот.
Вам сюда

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

11

Peter пишет:
drBatty пишет:
Peter пишет:

FILE *fin, *fout; llen:len = sizeof(struct sockaddr_in); if (len == 0) goto llen;

досвидания. Увидимся в следующей жизни, когда вы научитесь писать.

Это игра ума. Ещё раньше я замечал, что для struct sockaddr_in sizeof() часто возвращает
0 или -1, уже не помню. И зачем я буду себя ограничивать в коде?

Пётр.

да ладно! sizeof() действительно вычисляется не при компиляции, а при исполнении, но вернуть ерунду он никак не может. Во всяком случае переспрашивать его бесполезно. Кстати, объекты длины 0 вполне допустимы.
Но речь здесь о другом: этот кусок действительно странный, но он законный, так что обсуждать эту часть есть смысл только после того, как всё заработает.

12 (28.01.2015 23:00:44 отредактировано Peter)

s.xbatob пишет:
Peter пишет:
drBatty пишет:

досвидания. Увидимся в следующей жизни, когда вы научитесь писать.

Это игра ума. Ещё раньше я замечал, что для struct sockaddr_in sizeof() часто возвращает
0 или -1, уже не помню. И зачем я буду себя ограничивать в коде?

Пётр.

да ладно! sizeof() действительно вычисляется не при компиляции, а при исполнении, но вернуть ерунду он никак не может. Во всяком случае переспрашивать его бесполезно. Кстати, объекты длины 0 вполне допустимы.
Но речь здесь о другом: этот кусок действительно странный, но он законный, так что обсуждать эту часть есть смысл только после того, как всё заработает.

Замечал опять же в Ubuntu в этой программе. Однако этот кусок работает и в Ubuntu
и в Debian, поскольку выход из него происходит.

Пётр.

drBatty пишет:
Peter пишет:

И зачем я буду себя ограничивать в коде?

не, не нужно. Наоборот.
Вам сюда

Я там не вникал, но cin и cout с перенаправлением я никогда не использую,
употребляю только fprintf.

Пётр.

Пётр.

13

Peter, то, что ваш код "тут работает, там не работает", очевидно с первых строк. Можно даже не компилировать.

Peter пишет:

Я там не вникал

это сайт о том, как НЕ НАДО писать.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

14

drBatty пишет:

Peter, то, что ваш код "тут работает, там не работает", очевидно с первых строк. Можно даже не компилировать.

Peter пишет:

Я там не вникал

это сайт о том, как НЕ НАДО писать.

Это и по названию понятно. Обычно я стараюсь оптимизировать время выполнения.

Пётр.

Пётр.

15 (29.01.2015 13:16:05 отредактировано )

Peter пишет:

Обычно я стараюсь оптимизировать время выполнения.

преждевременная оптимизация — всегда зло(не я сказал).
И ваш "код" это доказывает на все 146%. Он не просто медленно работает, а часто вообще не работает.
И не нужно валить с больной головы на здоровую, Ubuntu тут совсем не причём.


PS:

Peter пишет:

Обычно я стараюсь оптимизировать время выполнения.

очевидно, вы даже не знаете, что это такое, "время выполнения". Доказательство

  llen:len = sizeof(struct sockaddr_in);
    if (len == 0)
    goto llen;

лично мне это УГ даже неловко в {code} оборачивать.

s.xbatob пишет:

sizeof() действительно вычисляется не при компиляции, а при исполнении

в C/C++ таки при компиляции. Это константа в рантайме. У вас опечатка что-ли?

s.xbatob пишет:

Кстати, объекты длины 0 вполне допустимы.

да. Как Невидимый Розовый Единорог.

s.xbatob пишет:

этот кусок действительно странный

он не странный, он не имеет смысла.

s.xbatob пишет:

но он законный

в том смысле, что существует компилятор, который из этого УГ может сделать какие-то байтики, а потом этим байтикам можно передать исполнение?

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

16

Я же сказал, что это игра ума. Тем более я не знал, почему sizeof() возвращала 0, вот и хотел это проверить - будет 0 или нет.
И это не настоящая программа для работы, она была написана для проверки одного предположения.

Пётр.

Пётр.

17

Peter пишет:

Я же сказал, что это игра ума

это плохая игра.

Peter пишет:

Тем более я не знал, почему sizeof() возвращала 0

во первых, sizeof ничего не возвращает, это константа. Но в отличиеот других констант, её значение не известно программисту во время написания кода.

Вот пример использования этой константы:

int* val = malloc(sizeof(int));

в коде никаких sizeof НЕ будет, а будет просто malloc(8), если у вас int имеет размер в 8 байт. Важно понимать, что sizeof возвращает размер в char'ах, а это не байты.

Во вторых, если у вас sizeof "возвращает" ноль, то вы явно что-то делаете не так. Зачем вам объект, которого нет?

В третьих, ваша "программа" не нужна(никому). Т.к. это write-only макароны.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

18

Рекомендую прочитать цикл статей о работе анализатора PVS-Studio.
Про lowlatency выше уже сказали вроде.

При укусе ядовитой змеи, держите её голову на расстоянии от себя, чтобы она не укусила вас в ответ...

19

Хорошо, я не задумывался, является ли sizeof() исполнимым оператором. До сих пор с ним не былотрудностей.

Пётр.

Пётр.

20

Peter пишет:

Хорошо, я не задумывался, является ли sizeof() исполнимым оператором. До сих пор с ним не былотрудностей.

зачем вы задумывались об оптимизации того, чего не понимаете?

goto, кстати, практически НЕ оптимизируется, а вот цикл while компилятор с радостью выкинет (потому, можно написать не нужный цикл, если вам так понятнее и "читаемие", но вот ненужное goto нельзя писать ни в коем разе).

Вот смотрите:

int factorial(int x)
{
    return x<=1? 1: x*factorial(x-1);
}

вы наверное скажите, что это "плохой код", да? Неоптимизированный, да? Работать будет медленно?

Хорошо, а вот этот:

 8048377:   bb 01 00 00 00          mov    ebx,0x1
 804837c:   eb 04                   jmp    8048382 <main+0x52>
 804837e:   66 90                   xchg   ax,ax
 8048380:   89 d0                   mov    eax,edx
 8048382:   8d 50 ff                lea    edx,[eax-0x1]
 8048385:   0f af d8                imul   ebx,eax
 8048388:   83 fa 01                cmp    edx,0x1
 804838b:   75 f3                   jne    8048380 <main+0x50>

как оно вам?

ВНЕЗАПНО: второй код — это и есть первый, только после обработки gcc,
Спрашивается, а зачем нужно было обмазываться goto, если компилятор сам умеет ими обмазываться?

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

21

Так это у меня была не настоящая рабочая программа, это наскоро сделанный
инструмент для проверки одного предположения. Если бы я знал, что sizeof()
выполняется на стадии компиляции, я бы, конечно, так не сделал, а проверил
бы как-то иначе, правильно он возвращает размер или нет.

goto я иногда использую, если это кажется удобным.
Для компилятора, наверное, проще обработать goto, чем while.


Я уже много лет не имел дела с этим ассемблером и даже
не знаю, что означает операнд в <>.
Я бы никак не подумал, что этот пример транслируется в такой короткий код.

Пётр.

Пётр.

22 (30.01.2015 09:18:53 отредактировано )

Peter пишет:

Если бы я знал, что sizeof()
выполняется на стадии компиляции, я бы, конечно, так не сделал, а проверил
бы как-то иначе

используйте assert - abort the program if assertion is false

+ открыть спойлер

ASSERT(3)                                          Linux Programmer's Manual                                          ASSERT(3)

NAME
       assert - abort the program if assertion is false

SYNOPSIS
       #include <assert.h>

       void assert(scalar expression);

DESCRIPTION
       If  the  macro  NDEBUG was defined at the moment <assert.h> was last included, the macro assert() generates no code, and
       hence does nothing at all.  Otherwise, the macro assert() prints an error message to standard error and  terminates  the
       program by calling abort(3) if expression is false (i.e., compares equal to zero).

       The  purpose  of  this  macro is to help programmers find bugs in their programs.  The message "assertion failed in file
       foo.c, function do_bar(), line 1287" is of no help at all to a user.

RETURN VALUE
       No value is returned.

зачем зацикливать-то?

Peter пишет:

goto я иногда использую, если это кажется удобным.
Для компилятора, наверное, проще обработать goto, чем while.

не проще, а наоборот — сложнее. Goto ломает контексты, т.е. если с while компилятор что-то может хранить в регистрах, то с goto НЕ может, ибо хрен его знает, куда оно прыгнет. Компилятор прост умывает руки, заталкивает всё в память, и прыгает, как в омут головой. А вот while он знает, и сам заменит её на goto, если это уместно.

Выше доказано, что компилятор даже НЕ хвостовую рекурсию в состоянии развернуть в цикл с goto.

Peter пишет:

Я уже много лет не имел дела с этим ассемблером и даже
не знаю, что означает операнд в <>.

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

Peter пишет:

Я бы никак не подумал, что этот пример транслируется в такой короткий код.

с рекурсией код был-бы не длиннее, но намного более медленнее. Тут фишка в том, что рекурсивный алгоритм напрямую тупо списан из учебника, где вводится это понятие(факториал), потому оно понятно. А то, что этот алгоритм не ложится на x86 — проблема компилятора.

Ещё заметьте, что эта функция тоже была выкинута нафиг. Компилятор встроил в код данный цикл. Откуда вывод: используйте функции, их читать легче. Не нужно думать, что компилятор не понимает, что call/ret занимают место и время, он понимает.

drBatty пишет:


8048377:   bb 01 00 00 00          mov    ebx,0x1; 1→ EBX
 804837c:   eb 04                   jmp    8048382 <main+0x52>; goto внутрь цикла
 804837e:   66 90                   xchg   ax,ax; NOP. Команда для выравнивания адреса. Цикл по адресу кратному 16 быстрее работает
 8048380:   89 d0                   mov    eax,edx; EDX→EAX
 8048382:   8d 50 ff                lea    edx,[eax-0x1]; EAX-1→EDX
 8048385:   0f af d8                imul   ebx,eax; EBX*=EAX
 8048388:   83 fa 01                cmp    edx,0x1
 804838b:   75 f3                   jne    8048380 <main+0x50>; if(EDX>=1) goto
Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

23

assert никогда не использовал, хотя у других видел. Он действует как-то грубо - сразу же заканчивает программу.

Если бы знал, что sizeof() выполняется на стадии компиляции, конечно, зацикливать бы не стал.
С goto в C как раз известно, куда он указывает, а в PL/1 и в Фортране есть переменные типа точки
входа, во всяком случае, в Фортране она может быть аргументом функции.

Я вообще избегаю сложных конструкций и стараюсь делать проще, чтобы было поменьше разветвлений.
И я не ожидал, что компилятор сумеет настолько оптимизировать код, что обойдётся без вызова функции.
Кажется, в самом конце 1990-х был случай, когда люди написали программу для DOS на ассемблере,
потом её же на C и компилятор Watcom построил код, работающий быстрее, чем самописный. Стали
разбираться и сравнивать, нашли, как он оптимизировал и как-то удалось улучшить ассемблерную
программу так, что она стала чуть быстрее, чем от Watcom.

Есть вообще такой стиль, как фунциональное программирование, когда всё делается через вызовы
функций, без условных операторов.

Пётр.

Пётр.

24

Peter пишет:

assert никогда не использовал, хотя у других видел. Он действует как-то грубо - сразу же заканчивает программу.

дык для отладки жеж.

If  the  macro  NDEBUG was defined at the moment <assert.h> was last included, the macro assert() generates no code, and
       hence does nothing at all.  Otherwise, the macro assert() prints an error message to standard error and  terminates  the
       program by calling abort(3) if expression is false (i.e., compares equal to zero).

Как раз для вашего случая: а вдруг sizeof вернёт ноль или минус 1???  ai
Ваша программа тупо зависнет.

Peter пишет:

С goto в C как раз известно, куда он указывает

это вам "известно". А что там с контекстом исполнения? А что с оптимизацией? Вы же видите, что компилятор рекурсию в цикл переводит? А если там goto, то не сможет. Так и останется медленная рекурсия.

Peter пишет:

Я вообще избегаю сложных конструкций и стараюсь делать проще, чтобы было поменьше разветвлений.
И я не ожидал, что компилятор сумеет настолько оптимизировать код, что обойдётся без вызова функции.

без вызова (inline) это фигня, такое уже 20 лет компилятор умеет. Вот рекурсия — интереснее. Лисповики-борщееды с ЛОРа молятся на TCO (хвостовая рекурсия), т.к. по их стандартам TC разворачивается. А у меня пруф того, что gcc умеет даже НЕ хвостовую (что в общем случае невозможно, кстати).

Peter пишет:

чтобы было поменьше разветвлений.

Не нужно. Компилятор уберёт ненужное. А вот обратного компилятор не умеет делать. И не факт, что на этом процессоре разворачивание дешевле. На x86 (любом) очень маленький кеш L1, да ещё и поделённый на линейки, если вы из него вылезете, то скорость упадёт в десятки и сотни раз!

Peter пишет:

Кажется, в самом конце 1990-х был

спасибо, я слышал эту байку, мне не 13 лет.

Peter пишет:

Есть вообще такой стиль, как фунциональное программирование, когда всё делается через вызовы
функций, без условных операторов.

вы бредите?
вот вам, изучайте:
ru.wikibooks.org/wiki/Реализации_алгоритмов/Факториал

вот общелисп:

(defun factorial (n)
   (if (= n 0)
       1
       (* (factorial (- n 1)) n)))

слово "if" хорошо видно?

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

25

Хорошо, буду знать ещё одну причину, чтобы не использовать goto.
Но в простых случаях это бывает удобно.

Всё же программа должна быть сама по себе хорошо написана, а не полагаться на то,
что её оптимизирует компилятор. Она при этом будет и лучше читаемой.

Была такая книжка - "Функциональное программирование", в мягкой коричневой обложке,
кажется, из серии "Библиотечка системного программиста".  То, что я привёл - это единственное,
что мне из неё запомнилось.

Пётр.

Пётр.

26

Peter пишет:

Хорошо, буду знать ещё одну причину, чтобы не использовать goto.

дело совсем не в goto. А в ломке контекстов. ФП как раз и придумано для изоляции контекстов внутри функций, т.е. функция ничего не видит и не знает, что там происходит снаружи. На C тоже так можно и нужно писать, т.е. не использовать goto, глобальные переменные, указатели, и т.п. (точнее, понимать, что это — плохо).

Peter пишет:

Но в простых случаях это бывает удобно.

__asm и вперёд.  bi

Peter пишет:

Всё же программа должна быть сама по себе хорошо написана, а не полагаться на то,
что её оптимизирует компилятор.

забудьте эту догму из 90. Сейчас другой век на дворе, откуда вам знать, что такое "хорошо" на целевой платформе?

Писать нужно так, что-бы это было
1. понятно человеку, который будет потом с матюгами править ваш код на целевой CPU
2. понятной компилятору, дабы компилятор хорошо генерировал код.

Peter пишет:

То, что я привёл - это единственное,
что мне из неё запомнилось.

лучше-бы вы её не читали. И да, почитайте SICP
http://lurkmore.to/SICP
(там есть даже видеолекции, для не умеющих читать. Есть и по-русски, варез тут запрещён, а мне лениво проверять, варез там или нет, потому прямые ссылки не дам).

И да, без условного оператора ни один ЯП не обходится, пусть даже в скрытом виде(например "условная функция").

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

27

Как-то странно слышать рекомендацию не использовать указатели в C/C++, тем более, что имя массива - тоже указатель.
Например, многопоточная программа, один поток пишет данные по указателю, переданному из другого.
Особенно, если это сотни килобайтов.

Вот просто написанная программа, без множества if/else и будет легко читаема и компилятор с ней не сделает
ничего неожиданного. Хорошо написанная - в прямом смысле, без лишних действий, без дублирования.

Пётр.

Пётр.

28

Peter пишет:

Как-то странно слышать рекомендацию не использовать указатели в C/C++

я знаю. И тем не менее. 40 лет назад, у вас просто не было другого выхода, а сейчас — есть. Компиляторы стали намного "умнее".

Peter пишет:

имя массива - тоже указатель.

во первых нет, если не верите, сделайте sizeof().
Кстати, массивы — как раз тот случай sizeof()==0

#include <stdio.h>

int main()
{
·   int a[0];
·   printf("%u %p\n", sizeof(a), a);
·   return 0;
}

Во вторых, массивы это тоже плохо, т.к. прибиты гвоздями к коду, из-за размера.

Peter пишет:

Например, многопоточная программа, один поток пишет данные по указателю, переданному из другого.

почему нельзя через файл?

Peter пишет:

Вот просто написанная программа, без множества if/else и будет легко читаема и компилятор с ней не сделает
ничего неожиданного. Хорошо написанная - в прямом смысле, без лишних действий, без дублирования.

может и сделает, с него станется…

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

29

Размер массива и так известен автору программы, зачем  к нему применять sizeof()?

Передавать данные через файл - очень странная рекомендация.
Даже если файл создать в памяти, это совершенно лишняя операция,
т.к. он уже находится в памяти и достаточно передать его адрес.

Компилятор, конечно, что-нибудь сделает, т.к. всё предусмотреть невозможно,
но важно, чтобы он не сделал нежелательного, поэтому и лучше без множества
проверок.

Пётр.

Пётр.

30

Peter пишет:

Размер массива и так известен автору программы, зачем  к нему применять sizeof()?

очевидно для того, что-бы писать код, который не зависит от размера массива. Например:

int main()
{
  int array[123];
  int j;
  for(j=0; j<sizeof(array)/sizeof(array[0]); j++)
    array[j] = 0;
  return 0;
}

если в определении массива поменять тип и/или размер, код всё равно будет корректно работать (исключение: если вы настолько упоролись, что сделали массив размером 0 эл-тов, код сломается).

Peter пишет:

Передавать данные через файл - очень странная рекомендация.

это очень хорошая и годная рекомендация.

Peter пишет:

Даже если файл создать в памяти, это совершенно лишняя операция,
т.к. он уже находится в памяти и достаточно передать его адрес.

естественно файл создаётся в памяти, и НЕ записывается на HDD.

Если вы используете указатель, то следить за ним только вам. Если сделали файл, то заботится о нём будет ОС. Зачем вам лишние сложности?

Указатель в памяти оправдан тогда, и только тогда, когда создание файла слишком дорогое удовольствие. Ну например если вы в маздае(хотя может я и не прав, не знаю, как там это сейчас). В Linux файлы создавать недорого.

Peter пишет:

Компилятор, конечно, что-нибудь сделает, т.к. всё предусмотреть невозможно,
но важно, чтобы он не сделал нежелательного, поэтому и лучше без множества
проверок.

ещё раз: без проверок код больше, и не влезет в L1. Также, если 10 раз копипастить одно и то же, то очень легко ошибиться. Особенно если потом поддерживать код. Лучше поручить эту тупую работу компилятору, который это(тупую работу) умеет лучше вас.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)