Тест, тест, тест, разбирать, разбирать, разбирать.
Простое целое число собственного размера.
unsigned int fun_one ( unsigned int a )
{
return((a&7)+1);
}
unsigned int fun_two ( unsigned int *a )
{
return((*a&7)+1);
}
Нет оптимизации, у вас есть одна дополнительная инструкция при передаче по ссылке, чтобы загрузить значение по этому адресу и что-то с ним сделать.
00000000 :
0: e52db004 push {fp} ; (str fp, [sp, #-4]!)
4: e28db000 add fp, sp, #0
8: e24dd00c sub sp, sp, #12
c: e50b0008 str r0, [fp, #-8]
10: e51b3008 ldr r3, [fp, #-8]
14: e2033007 and r3, r3, #7
18: e2833001 add r3, r3, #1
1c: e1a00003 mov r0, r3
20: e28bd000 add sp, fp, #0
24: e49db004 pop {fp} ; (ldr fp, [sp], #4)
28: e12fff1e bx lr
0000002c :
2c: e52db004 push {fp} ; (str fp, [sp, #-4]!)
30: e28db000 add fp, sp, #0
34: e24dd00c sub sp, sp, #12
38: e50b0008 str r0, [fp, #-8]
3c: e51b3008 ldr r3, [fp, #-8]
40: e5933000 ldr r3, [r3]
44: e2033007 and r3, r3, #7
48: e2833001 add r3, r3, #1
4c: e1a00003 mov r0, r3
50: e28bd000 add sp, fp, #0
54: e49db004 pop {fp} ; (ldr fp, [sp], #4)
58: e12fff1e bx lr
Оптимизация, от -O1 до -O3 дали тот же результат.И вы по-прежнему теряете инструкцию, загружающую значение.
00000000 :
0: e2000007 and r0, r0, #7
4: e2800001 add r0, r0, #1
8: e12fff1e bx lr
0000000c :
c: e5900000 ldr r0, [r0]
10: e2000007 and r0, r0, #7
14: e2800001 add r0, r0, #1
18: e12fff1e bx lr
И это будет продолжаться так же, как и для практически любой единицы измерения, которую вы можете передать. 64-битные целые числа, вы все еще сжигаете дополнительную инструкцию изагрузка циклов памяти из ссылки в регистры для работы.Любой массив чего-то, что вы действительно не можете сделать, передавая по значению, не так ли?Но структура, которую вы можете, и для достижения структуры, ссылки или нет, вероятно, потребует некоторой адресации.
typedef struct
{
unsigned int a;
unsigned int b;
char c[4];
} ruct;
unsigned int fun_one ( ruct a )
{
return((a.c[3]&7)+1);
}
unsigned int fun_two ( ruct *a )
{
return((a->c[3]&7)+1);
}
Без оптимизации мы начнем с привязки по 12 инструкций каждая.Я должен был бы смотреть на это больше, чтобы решить, если один из них сжигает больше тактов, чем другой.
00000000 :
0: e52db004 push {fp} ; (str fp, [sp, #-4]!)
4: e28db000 add fp, sp, #0
8: e24dd014 sub sp, sp, #20
c: e24b3010 sub r3, fp, #16
10: e8830007 stm r3, {r0, r1, r2}
14: e55b3005 ldrb r3, [fp, #-5]
18: e2033007 and r3, r3, #7
1c: e2833001 add r3, r3, #1
20: e1a00003 mov r0, r3
24: e28bd000 add sp, fp, #0
28: e49db004 pop {fp} ; (ldr fp, [sp], #4)
2c: e12fff1e bx lr
00000030 :
30: e52db004 push {fp} ; (str fp, [sp, #-4]!)
34: e28db000 add fp, sp, #0
38: e24dd00c sub sp, sp, #12
3c: e50b0008 str r0, [fp, #-8]
40: e51b3008 ldr r3, [fp, #-8]
44: e5d3300b ldrb r3, [r3, #11]
48: e2033007 and r3, r3, #7
4c: e2833001 add r3, r3, #1
50: e1a00003 mov r0, r3
54: e28bd000 add sp, fp, #0
58: e49db004 pop {fp} ; (ldr fp, [sp], #4)
5c: e12fff1e bx lr
Но посмотрите, что происходит с оптимизацией.Структура была рассчитана таким образом, что при передаче она помещалась в регистры.
00000000 :
0: e24dd010 sub sp, sp, #16
4: e28d3004 add r3, sp, #4
8: e8830007 stm r3, {r0, r1, r2}
c: e5dd100f ldrb r1, [sp, #15]
10: e2010007 and r0, r1, #7
14: e2800001 add r0, r0, #1
18: e28dd010 add sp, sp, #16
1c: e12fff1e bx lr
00000020 :
20: e5d0100b ldrb r1, [r0, #11]
24: e2010007 and r0, r1, #7
28: e2800001 add r0, r0, #1
2c: e12fff1e bx lr
К сожалению, gcc не очень хорошо справился с оптимизацией этого, мог бы выполнить сдвиг и, и в одной инструкции на r3,добавьте, и bx, lr, три инструкции, обгоняя проход по ссылке.
Вам нужно знать компилятор и интерфейс, он передает аргументы в регистрах или всегда в стеке?Если используются регистры, что он делает, если вашим аргументам нужно больше места, чем могут обработать зарезервированные регистры, заполняет ли он их, затем использует стек, просто использует стек и не регистры?Передает ли он указатель на память, содержащую аргумент, передается по ссылочному стилю, но так, чтобы передаваемое значение было защищено.
Вам также нужно смотреть за пределы отдельных функций относительно того, сколько памяти и работы регистра имеетслучиться, чтобы подготовить вызов функции.Передача по ссылке для примера структуры будет одной загрузкой или немедленной, чтобы заполнить один регистр адресом структуры.Передача по значению структуры, в случае ARM, была бы единственной инструкцией для загрузки трех регистров со структурой, но потенциально она занимает три такта (или 6 или 2 в зависимости от шины amba / axi).Другие процессоры могут стоить вам три инструкции плюс тактовый цикл данных для каждого регистра.Таким образом, даже если бы gcc лучше справился с оптимизацией примера структуры передачи по значению, передача по ссылке могла бы просто вытеснить его за один или два такта, но это сильно зависит от того, как выглядит код в вызывающей функции.Чтобы действительно знать, что вы должны протестировать его путем точной синхронизации кода и разобрать, чтобы понять, почему он становится быстрее или медленнее, когда вы настраиваете его.