Необходимо развернуть циклы макросов
Рассмотрим следующий код:
extern int conn_fds[MAX_CLIENTS];
extern int fl_conn_indexes[MAX_CLIENTS];
extern int fl_req_bufs[MAX_CLIENTS];
extern struct epoll_event estab_events[MAX_THREADS];
extern req_buf_t req_bufs[MAX_REQ_BUFS];
extern int fl_req_bufs_top;
extern int conn_statuses[MAX_CLIENTS];
extern int fl_conn_indexes_top;
extern tcpl_nc_t nc_http_list;
extern struct sockaddr_in conn_addresses[MAX_CLIENTS];
void accept_connections(unsigned int num_conns) {
int fds[MAX_THREADS];
int conn_indexes[MAX_THREADS];
int conn_idx=0;
int new_bottom;
socklen_t slenghts[MAX_THREADS];
void *labels1[MAX_THREADS] = {&&a0,&&a1,&&a2,&&a3,&&a4,&&a5,&&a6,&&a7,&&a8,&&a9,&&a10,&&a11,&&a12,&&a13,&&a14,&&a15};
void *labels2[MAX_THREADS] = {&&b0,&&b1,&&b2,&&b3,&&b4,&&b5,&&b6,&&b7,&&b8,&&b9,&&b10,&&b11,&&b12,&&b13,&&b14,&&b15};
void *labels3[MAX_THREADS] = {&&c0,&&c1,&&c2,&&c3,&&c4,&&c5,&&c6,&&c7,&&c8,&&c9,&&c10,&&c11,&&c12,&&c13,&&c14,&&c15};
new_bottom=fl_conn_indexes_top-num_conns;
if (new_bottom<=0) return;
goto *labels1[num_conns];
a15:
conn_indexes[MAX_THREADS- 1]=fl_conn_indexes[fl_conn_indexes_top- 1];
a14:
conn_indexes[MAX_THREADS- 2]=fl_conn_indexes[fl_conn_indexes_top- 2];
a13:
conn_indexes[MAX_THREADS- 3]=fl_conn_indexes[fl_conn_indexes_top- 3];
a12:
conn_indexes[MAX_THREADS- 4]=fl_conn_indexes[fl_conn_indexes_top- 4];
a11:
conn_indexes[MAX_THREADS- 5]=fl_conn_indexes[fl_conn_indexes_top- 5];
a10:
conn_indexes[MAX_THREADS- 6]=fl_conn_indexes[fl_conn_indexes_top- 6];
a9:
conn_indexes[MAX_THREADS- 7]=fl_conn_indexes[fl_conn_indexes_top- 7];
a8:
conn_indexes[MAX_THREADS- 8]=fl_conn_indexes[fl_conn_indexes_top- 8];
a7:
conn_indexes[MAX_THREADS- 9]=fl_conn_indexes[fl_conn_indexes_top- 9];
a6:
conn_indexes[MAX_THREADS-10]=fl_conn_indexes[fl_conn_indexes_top-10];
a5:
conn_indexes[MAX_THREADS-11]=fl_conn_indexes[fl_conn_indexes_top-11];
a4:
conn_indexes[MAX_THREADS-12]=fl_conn_indexes[fl_conn_indexes_top-12];
a3:
conn_indexes[MAX_THREADS-13]=fl_conn_indexes[fl_conn_indexes_top-13];
a2:
conn_indexes[MAX_THREADS-14]=fl_conn_indexes[fl_conn_indexes_top-14];
a1:
conn_indexes[MAX_THREADS-15]=fl_conn_indexes[fl_conn_indexes_top-15];
a0:
conn_indexes[MAX_THREADS-16]=fl_conn_indexes[fl_conn_indexes_top-16];
fl_conn_indexes_top=fl_conn_indexes_top-num_conns;
goto *labels2[num_conns];
b15:
conn_fds[conn_indexes[MAX_THREADS- 1]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS- 1]],&slenghts[MAX_THREADS- 1]);
b14:
conn_fds[conn_indexes[MAX_THREADS- 2]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS- 2]],&slenghts[MAX_THREADS- 2]);
b13:
conn_fds[conn_indexes[MAX_THREADS- 3]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS- 3]],&slenghts[MAX_THREADS- 3]);
b12:
conn_fds[conn_indexes[MAX_THREADS- 4]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS- 4]],&slenghts[MAX_THREADS- 4]);
b11:
conn_fds[conn_indexes[MAX_THREADS- 5]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS- 5]],&slenghts[MAX_THREADS- 5]);
b10:
conn_fds[conn_indexes[MAX_THREADS- 6]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS- 6]],&slenghts[MAX_THREADS- 6]);
b9:
conn_fds[conn_indexes[MAX_THREADS- 7]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS- 7]],&slenghts[MAX_THREADS- 7]);
b8:
conn_fds[conn_indexes[MAX_THREADS- 8]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS- 8]],&slenghts[MAX_THREADS- 8]);
b7:
conn_fds[conn_indexes[MAX_THREADS- 9]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS- 9]],&slenghts[MAX_THREADS- 9]);
b6:
conn_fds[conn_indexes[MAX_THREADS-10]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS-10]],&slenghts[MAX_THREADS-10]);
b5:
conn_fds[conn_indexes[MAX_THREADS-11]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS-11]],&slenghts[MAX_THREADS-11]);
b4:
conn_fds[conn_indexes[MAX_THREADS-12]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS-12]],&slenghts[MAX_THREADS-12]);
b3:
conn_fds[conn_indexes[MAX_THREADS-13]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS-13]],&slenghts[MAX_THREADS-13]);
b2:
conn_fds[conn_indexes[MAX_THREADS-14]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS-14]],&slenghts[MAX_THREADS-14]);
b1:
conn_fds[conn_indexes[MAX_THREADS-15]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS-15]],&slenghts[MAX_THREADS-15]);
b0:
conn_fds[conn_indexes[MAX_THREADS-16]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS-16]],&slenghts[MAX_THREADS-16]);
goto *labels3[num_conns];
c15:
conn_statuses[conn_indexes[MAX_THREADS- 1]]=CONN_STATUS_READING;
c14:
conn_statuses[conn_indexes[MAX_THREADS- 2]]=CONN_STATUS_READING;
c13:
conn_statuses[conn_indexes[MAX_THREADS- 3]]=CONN_STATUS_READING;
c12:
conn_statuses[conn_indexes[MAX_THREADS- 4]]=CONN_STATUS_READING;
c11:
conn_statuses[conn_indexes[MAX_THREADS- 5]]=CONN_STATUS_READING;
c10:
conn_statuses[conn_indexes[MAX_THREADS- 6]]=CONN_STATUS_READING;
c9:
conn_statuses[conn_indexes[MAX_THREADS- 7]]=CONN_STATUS_READING;
c8:
conn_statuses[conn_indexes[MAX_THREADS- 8]]=CONN_STATUS_READING;
c7:
conn_statuses[conn_indexes[MAX_THREADS- 9]]=CONN_STATUS_READING;
c6:
conn_statuses[conn_indexes[MAX_THREADS-10]]=CONN_STATUS_READING;
c5:
conn_statuses[conn_indexes[MAX_THREADS-11]]=CONN_STATUS_READING;
c4:
conn_statuses[conn_indexes[MAX_THREADS-12]]=CONN_STATUS_READING;
c3:
conn_statuses[conn_indexes[MAX_THREADS-13]]=CONN_STATUS_READING;
c2:
conn_statuses[conn_indexes[MAX_THREADS-14]]=CONN_STATUS_READING;
c1:
conn_statuses[conn_indexes[MAX_THREADS-15]]=CONN_STATUS_READING;
c0:
conn_statuses[conn_indexes[MAX_THREADS-16]]=CONN_STATUS_READING;
}
Здесь я параллельно обрабатываю количество подключений MAX_THREADS. Петли были развернуты специально для скорости. Но код очень длинный. Мне нужно уменьшить его с помощью макросов, вроде этого:
extern int conn_fds[MAX_CLIENTS];
extern int fl_conn_indexes[MAX_CLIENTS];
extern int fl_req_bufs[MAX_CLIENTS];
extern struct epoll_event estab_events[MAX_THREADS];
extern req_buf_t req_bufs[MAX_REQ_BUFS];
extern int fl_req_bufs_top;
extern int conn_statuses[MAX_CLIENTS];
extern int fl_conn_indexes_top;
extern tcpl_nc_t nc_http_list;
extern struct sockaddr_in conn_addresses[MAX_CLIENTS];
void accept_connections(unsigned int num_conns) {
int fds[MAX_THREADS];
int conn_indexes[MAX_THREADS];
int conn_idx=0;
int new_bottom;
socklen_t slenghts[MAX_THREADS];
void *labels1[MAX_THREADS] = {&&a0,&&a1,&&a2,&&a3,&&a4,&&a5,&&a6,&&a7,&&a8,&&a9,&&a10,&&a11,&&a12,&&a13,&&a14,&&a15};
void *labels2[MAX_THREADS] = {&&b0,&&b1,&&b2,&&b3,&&b4,&&b5,&&b6,&&b7,&&b8,&&b9,&&b10,&&b11,&&b12,&&b13,&&b14,&&b15};
void *labels3[MAX_THREADS] = {&&c0,&&c1,&&c2,&&c3,&&c4,&&c5,&&c6,&&c7,&&c8,&&c9,&&c10,&&c11,&&c12,&&c13,&&c14,&&c15};
new_bottom=fl_conn_indexes_top-num_conns;
if (new_bottom<=0) return;
goto *labels1[num_conns];
LOOP(1,MAX_THREADS) {
a{ITERATOR}:
conn_indexes[MAX_THREADS- {ITERATOR}]=fl_conn_indexes[fl_conn_indexes_top- {ITERATOR}];
}
fl_conn_indexes_top=fl_conn_indexes_top-num_conns;
goto *labels2[num_conns];
LOOP(1,MAX_THREADS) {
b{ITERATOR}:
conn_fds[conn_indexes[MAX_THREADS- {ITERATOR}]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[MAX_THREADS- 1{ITERATOR}]],&slenghts[MAX_THREADS- {ITERATOR}]);
}
goto *labels3[num_conns];
LOOP(1,MAX_THREADS) {
c{ITERATOR}:
conn_statuses[conn_indexes[MAX_THREADS- {ITERATOR}]]=CONN_STATUS_READING;
}
}
{ITERATOR} идет от 1 до MAX_THREADS внутри LOOP(). Метки нужны, потому что количество соединений может быть меньше, чем MAX_THREADS, и я должен пропустить пустые переменные, в противном случае я получу segfaults.
Может ли это быть легко реализовано с помощью препроцессора GCC? Если нет, возможно, я мог бы использовать m4, но как интегрировать m4 с GCC для комфортной разработки. Потому что я собираюсь редактировать эти макросы очень часто вместе со многими другими файлами в исходном коде, и необходима легкость кодирования.
Буду очень признателен за вашу помощь
2 ответа
Если вам требуется ответ в m4
:
dnl usage: unroll(var, initial, max, code)
dnl e.g. unroll(`i', 0, 10, ``total += arr[i]'')
dnl The above goes from 0 to 9, it does not include the final 10.
define(`unroll',
`define(`$1', `$2')ifelse(eval($2 < $3),
1,
`$4`'unroll(`$1',incr($2),$3,`$4')',
`undefine(`$1')')')dnl
void accept_connections(unsigned int num_conns) {
int fds[MAX_THREADS];
int conn_indexes[MAX_THREADS];
int conn_idx=0;
int new_bottom;
socklen_t slenghts[MAX_THREADS];
void *labels1[MAX_THREADS] = {unroll(`i', 0, 16, `&&a`'i,')};
void *labels2[MAX_THREADS] = {unroll(`i', 0, 16, `&&b`'i,')};
void *labels3[MAX_THREADS] = {unroll(`i', 0, 16, `&&c`'i,')};
new_bottom=fl_conn_indexes_top-num_conns;
if (new_bottom<=0) return;
define(`MAX_NUM_CONNS', 16)
goto *labels1[num_conns];
unroll(`count', 1, incr(MAX_NUM_CONNS()), `dnl
a`'eval(MAX_NUM_CONNS() - count):
conn_indexes[MAX_THREADS - count]=fl_conn_indexes[fl_conn_indexes_top - count];
')dnl
fl_conn_indexes_top=fl_conn_indexes_top-num_conns;
goto *labels2[num_conns];
unroll(`count', 1, incr(MAX_NUM_CONNS()), `dnl
define(`index', `MAX_THREADS - count')dnl
b`'eval(MAX_NUM_CONNS() - count):
conn_fds[conn_indexes[index]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[index]],&slenghts[index]);
')dnl
undefine(`index')
goto *labels3[num_conns];
unroll(`count', 1, incr(MAX_NUM_CONNS()), `dnl
c`'eval(MAX_NUM_CONNS() - count):
conn_statuses[conn_indexes[MAX_THREADS - count]]=CONN_STATUS_READING;
')dnl
}
Обратите особое внимание на макросы теперь в коде: define
, unroll
, eval
, MAX_NUM_CONNS
, count
, index
, i
, Также обратите внимание на причудливые цитаты, которые нравятся m4.
Я могу предложить улучшение этого (независимо от того, используете ли вы m4
). Ваш пример использования gotos, следующих сразу за друг другом, уже точно описан более известной структурой управления: switch
(потому что это стандартное поведение без break
):
define(`MAX_NUM_CONNS', 16)
switch (num_conns) {
unroll(`count', 1, incr(MAX_NUM_CONNS()), `dnl
case eval(MAX_NUM_CONNS() - count):
conn_indexes[MAX_THREADS - count]=fl_conn_indexes[fl_conn_indexes_top - count];
')dnl
}
fl_conn_indexes_top=fl_conn_indexes_top-num_conns;
switch (num_conns) {
unroll(`count', 1, incr(MAX_NUM_CONNS()), `dnl
define(`index', `MAX_THREADS - count')dnl
case eval(MAX_NUM_CONNS() - count):
conn_fds[conn_indexes[index]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[index]],&slenghts[index]);
')dnl
}
undefine(`index')
switch (num_conns) {
unroll(`count', 1, incr(MAX_NUM_CONNS()), `dnl
case eval(MAX_NUM_CONNS() - count):
conn_statuses[conn_indexes[MAX_THREADS - count]]=CONN_STATUS_READING;
')dnl
}
Если вы выполните это преобразование в коммутатор, вы можете удалить массивы меток.
Наконец, возможно, это может быть один переключатель:
define(`MAX_NUM_CONNS', 16)
switch (num_conns) {
unroll(`count', 1, incr(MAX_NUM_CONNS()), `dnl
define(`index', `MAX_THREADS - count')dnl
case eval(MAX_NUM_CONNS() - count):
conn_indexes[index]=fl_conn_indexes[fl_conn_indexes_top - count];
conn_fds[conn_indexes[index]]=accept(nc_http_list.sock_fd,(struct sockaddr*) &conn_addresses[conn_indexes[index]],&slenghts[index]);
conn_statuses[conn_indexes[index]]=CONN_STATUS_READING;
')dnl
}
undefine(`index')
fl_conn_indexes_top=fl_conn_indexes_top-num_conns;
Обратите внимание, что я переместил fl_conn_indexes_top
модификация после того, как весь развернутый цикл закончен - я не уверен, что это допустимо, потому что это зависит от того, когда эта переменная считывается или записывается. Вы можете сделать решение позвонить туда.
Как я уже упоминал в комментарии, это можно сделать и с помощью флагов компилятора. GCC поддерживает -funroll-loops
опция, которая делает это для вас на уровне компилятора. GCC также поддерживает -fprofile-use
, который (при подаче профиля из -fprofile-generate
) развернет циклы, которые оказались причиной проблем с производительностью.
В заключение:
Я думаю, что это все преждевременная оптимизация. Я сильно сомневаюсь, что проверка сравнения цикла является узким местом, когда каждая итерация вызываетaccept
!
Вы можете использовать препроцессор C с помощью библиотеки препроцессора chaos-pp
#include <chaos/preprocessor/arithmetic/dec.h>
#include <chaos/preprocessor/config.h>
#include <chaos/preprocessor/lambda/ops.h>
#include <chaos/preprocessor/logical/bool.h>
#include <chaos/preprocessor/recursion/expr.h>
#include <chaos/preprocessor/repetition/for.h>
#define PRED(s, x) CHAOS_PP_BOOL(x)
#define OP(s, x) CHAOS_PP_DEC(x)
#define CONN_IDX(s , idx) \
a##idx: \
conn_indexes[MAX_THREADS - (16 - idx)]=fl_conn_indexes[fl_conn_indexes_top - (16 - idx)];
CHAOS_PP_EXPR(CHAOS_PP_FOR(PRED, OP, CONN_IDX, 15))
(Отформатированный) вывод:
a15:
conn_indexes[MAX_THREADS - (16 - 15)]=fl_conn_indexes[fl_conn_indexes_top - (16 - 15)];
a14:
conn_indexes[MAX_THREADS - (16 - 14)]=fl_conn_indexes[fl_conn_indexes_top - (16 - 14)];
...