templatetypedef подвел итог.Чтобы добавить поддержку своего ответа.Возьмем следующие примеры функций:
unsigned int fun1 ( unsigned int *x )
{
unsigned int ra,rb;
rb=0;
for(ra=0;ra<1000;ra++) rb+=*x++;
return(rb);
}
unsigned int fun2 ( unsigned int *x )
{
unsigned int ra,rb;
rb=0;
for(ra=0;ra<1000;ra++) rb+=x[ra];
return(rb);
}
Теперь gcc произвел это:
00000000 fun1:
0: e52d4004 push {r4} ; (str r4, [sp, #-4]!)
4: e1a03000 mov r3, r0
8: e2804efa add r4, r0, #4000 ; 0xfa0
c: e3a00000 mov r0, #0
10: e1a02003 mov r2, r3
14: e492c004 ldr ip, [r2], #4
18: e5931004 ldr r1, [r3, #4]
1c: e2823004 add r3, r2, #4
20: e080000c add r0, r0, ip
24: e1530004 cmp r3, r4
28: e0800001 add r0, r0, r1
2c: 1afffff7 bne 10
30: e49d4004 pop {r4} ; (ldr r4, [sp], #4)
34: e12fff1e bx lr
00000038 fun2:
38: e3a03000 mov r3, #0
3c: e1a02003 mov r2, r3
40: e790c003 ldr ip, [r0, r3]
44: e2833004 add r3, r3, #4
48: e7901003 ldr r1, [r0, r3]
4c: e2833004 add r3, r3, #4
50: e082200c add r2, r2, ip
54: e3530efa cmp r3, #4000 ; 0xfa0
58: e0822001 add r2, r2, r1
5c: 1afffff7 bne 40
60: e1a00002 mov r0, r2
64: e12fff1e bx lr
Код другой, но я удивлен упущенными возможностями для оптимизации.
Clang / llvm произвел это:
00000000 fun1:
0: e3a01000 mov r1, #0
4: e3a02ffa mov r2, #1000 ; 0x3e8
8: e1a03001 mov r3, r1
c: e2522001 subs r2, r2, #1
10: e490c004 ldr ip, [r0], #4
14: e08c3003 add r3, ip, r3
18: e2c11000 sbc r1, r1, #0
1c: e182c001 orr ip, r2, r1
20: e35c0000 cmp ip, #0
24: 1afffff8 bne c
28: e1a00003 mov r0, r3
2c: e12fff1e bx lr
00000030 fun2:
30: e3a01000 mov r1, #0
34: e3a02ffa mov r2, #1000 ; 0x3e8
38: e1a03001 mov r3, r1
3c: e2522001 subs r2, r2, #1
40: e490c004 ldr ip, [r0], #4
44: e08c3003 add r3, ip, r3
48: e2c11000 sbc r1, r1, #0
4c: e182c001 orr ip, r2, r1
50: e35c0000 cmp ip, #0
54: 1afffff8 bne 3c
58: e1a00003 mov r0, r3
5c: e12fff1e bx lr
Вы можете заметить, что компилятор выдает точно такой же код, указатель или смещение.И меняя компиляторы, я чувствовал себя лучше, чем менять указатель на индексирование массива.Я думаю, что llvm мог бы сделать немного лучше, мне нужно изучить это немного больше, чтобы понять, что мой код сделал, чтобы вызвать это.
РЕДАКТИРОВАТЬ:
Я надеялся получить компилятор вкак минимум, используйте инструкцию ldr rd, [rs], # 4, которая поддерживает указатели, и надеется, что компилятор увидит, что он может уничтожить адрес массива, таким образом рассматривая его как указатель, а не как смещение в массиве (и используйте вышеупомянутую инструкцию, что в основном то, что сделал clang / llvm).Или, если бы он сделал массив, он бы использовал инструкцию ldr rd, [rm, rn].По сути, он надеялся, что один из компиляторов сгенерирует одно из этих решений:
funa:
mov r1,#0
mov r2,#1000
funa_loop:
ldr r3,[r0],#4
add r1,r1,r3
subs r2,r2,#1
bne funa_loop
mov r0,r1
bx lr
funb:
mov r1,#0
mov r2,#0
funb_loop:
ldr r3,[r0,r2]
add r1,r1,r3
add r2,r2,#4
cmp r2,#0x4000
bne funb_loop
mov r0,r1
bx lr
func:
mov r1,#0
mov r2,#4000
subs r2,r2,#4
func_loop:
beq func_done
ldr r3,[r0,r2]
add r1,r1,r3
subs r2,r2,#4
b func_loop
func_done:
mov r0,r1
bx lr
Не совсем понял, но подошел довольно близко.Это было забавное упражнение.Обратите внимание, что выше все ARM ассемблер.
В общем, (не мой конкретный пример кода C и не обязательно ARM), ряд популярных архитектур, которые вы будете загружать с адреса на основе регистра (ldr r0, [r1]) и загрузкас индексом / смещением регистра (ldr r0, [r1, r2]), где адрес является суммой двух регистров.один регистр в идеале является базовым адресом массива, а второй - индексом / смещением.Первая загрузка из регистра поддается указателям, вторая - массивам.если ваша C-программа НЕ собирается изменять или перемещать указатель или индекс, то в обоих случаях это означает, что вычисляется статический адрес, а затем используется нормальная загрузка, и массив, и указатель должны выдавать одинаковые инструкции.Для более интересного случая изменения указателя / индекса.
Pointer
ldr r0,[r1]
...
add r1,r1,some number
Array index
ldr r0,[r1,r2]
...
add r2,r2,some number
(при необходимости замените загрузку хранилищем, а добавление - подпрограммой)
В некоторых архитектурах нет трехзарегистрировать инструкцию индекса регистра, так что вы должны сделать что-то вроде
array index:
mov r2,r1
...
ldr r0,[r2]
...
add r2,r2,some number
Или, в зависимости от компилятора, это может быть очень плохо, особенно если вы компилируете для отладки или без оптимизации, и предполагая, что у вас нет трех регистровдобавить
array index:
mov r2,#0
...
mov r3,r1
add r3,r2
ldr r4,[r3]
...
add r2,some number
Так что вполне возможно, что оба подхода равны.Как видно на ARM, он может объединять две (в пределах, ограниченных для непосредственных) инструкций указателя в одну, что делает это немного быстрее.Решение индекса массива сжигает больше регистров, и в зависимости от количества доступных регистров для архитектуры, которая подталкивает вас к необходимости быстрее и чаще выгружать регистры в стек (чем вы с указателями), замедляя вас еще больше.Если вы не возражаете против уничтожения базового адреса, нижняя строка - это решение для указателя может дать вам преимущество с точки зрения производительности.Это во многом связано с вашим кодом и компилятором.Для меня это удобочитаемость, и я чувствую, что массивы легче читать и отслеживать, а во-вторых, мне нужно сохранить этот указатель, чтобы освободить malloc или снова пройти через эту память и т. Д. Если это так, я, вероятно, буду использовать массив синдекс, если это однократный проход, и я не забочусь об уничтожении базового адреса, я буду использовать указатель.Как вы видели выше в коде, сгенерированном компилятором, если производительность критична, то в любом случае вручную закодируйте решение на ассемблере (основываясь на предложенных подходах, позволив компиляторам попробовать это в первую очередь).