У меня есть микроконтроллер, у которого нет MMU, но мы используем C и C ++.
Мы избегаем всего динамического использования памяти (то есть нет new SomeClass()
или же malloc()
) и большая часть стандартной библиотеки.
Полу-вопрос 0:
Из того, что я понимаю std::array
не использует динамическую память, поэтому ее использование должно быть в порядке (только в стеке). Смотря на std::array
Исходный код выглядит хорошо, так как создает массив в стиле c, а затем оборачивает функциональность вокруг этого массива.
Чип, который мы используем, имеет 1 МБ флэш-памяти для хранения кода.
Вопрос 1:
Я обеспокоен тем, что использование шаблонов в std::array
приведет к тому, что двоичный файл будет больше, что может привести к превышению предела памяти кода в 1 МБ.
Я думаю, что если вы создадите экземпляр std::array< int, 5 >
, то все вызовы функций на что std::array
будет занимать определенный объем памяти кода, скажем, X байт памяти.
Если вы создаете другой экземпляр std::array< SomeObject, 5 >
затем вызовите функции к этому std::array
Будет ли каждая из этих функций теперь дублироваться в двоичном коде, занимая при этом больше памяти кода? X байт памяти + Y байт памяти.
Если да, думаете ли вы, что объем кода, сгенерированный с учетом ограниченного объема памяти кода, будет проблемой?
Вопрос 2:
В приведенном выше примере, если вы создали второй std::array< int, 10 >
Например, будут ли вызовы функций дублировать вызовы функций в сгенерированном коде? Хотя оба экземпляра одного типа, int
?
std::array
считается абстракцией с нулевой стоимостью, что означает, что она должна быть довольно оптимизируемой компилятором.
Как и в случае любой абстракции с нулевой стоимостью, она может вызывать небольшую пенальтию времени компиляции, и если требуемые оптимизации действительно нулевые затраты не поддерживаются, то это может привести к небольшому размеру или штрафу времени выполнения.
Тем не менее, обратите внимание, что компилятор может добавлять отступы в конце структуры. поскольку std::array
это структура, вы должны проверить, как ваша платформа обрабатывает std::array
Но я очень сомневаюсь, что это так для вас.
Возьми этот массив и std::array
дело:
#include <numeric>
#include <iterator>
template<std::size_t n>
int stuff(const int(&arr)[n]) {
return std::accumulate(std::begin(arr), std::end(arr), 0);
}
int main() {
int arr[] = {1, 2, 3, 4, 5, 6};
return stuff(arr);
}
#include <numeric>
#include <iterator>
#include <array>
template<std::size_t n>
int stuff(const std::array<int, n>& arr) {
return std::accumulate(std::begin(arr), std::end(arr), 0);
}
int main() {
std::array arr = {1, 2, 3, 4, 5, 6};
return stuff(arr);
}
Clang поддерживает это дело очень хорошо. все случаи с std::array
или необработанные массивы обрабатываются одинаково:
-O2
/ -O3
и массив и std::array
с лязгом:
main: # @main
mov eax, 21
ret
Однако у GCC, похоже, есть проблемы с его оптимизацией, поскольку std::array
и случай необработанного массива:
-O3
с GCC для массива и std::array
:
main:
movdqa xmm0, XMMWORD PTR .LC0[rip]
movaps XMMWORD PTR [rsp-40], xmm0
mov edx, DWORD PTR [rsp-32]
mov eax, DWORD PTR [rsp-28]
lea eax, [rdx+14+rax]
ret
.LC0:
.long 1
.long 2
.long 3
.long 4
Тогда, кажется, лучше оптимизировать с -O2
в случае необработанного массива и сбой с std::array
:
-O2
НКУ std::array
:
main:
movabs rax, 8589934593
lea rdx, [rsp-40]
mov ecx, 1
mov QWORD PTR [rsp-40], rax
movabs rax, 17179869187
mov QWORD PTR [rsp-32], rax
movabs rax, 25769803781
lea rsi, [rdx+24]
mov QWORD PTR [rsp-24], rax
xor eax, eax
jmp .L3
.L5:
mov ecx, DWORD PTR [rdx]
.L3:
add rdx, 4
add eax, ecx
cmp rdx, rsi
jne .L5
rep ret
-O2
Необработанный массив GCC:
main:
mov eax, 21
ret
Похоже, что ошибка GCC не удалось оптимизировать -O3
но преуспеть с -O2
исправлено в самой последней сборке.
Вот проводник компилятора со всеми O2
и O3
С учетом всех этих случаев вы можете увидеть общую картину: никакой информации о std::array
выводится в двоичном виде. Там нет конструкторов, нет operator[]
, даже не итераторы, ни алгоритмы. Все встроено. Компилятор хорош во вложении простых функций. std::array
функции-члены обычно очень очень просты.
Если вы создаете другой экземпляр std :: array< SomeObject, 5>, затем вызывает функции для этого std :: array, будет ли каждая из этих функций теперь дублироваться в двоичном файле, занимая при этом больше флэш-памяти? X байт памяти + Y байт памяти.
Итак, вы изменили тип данных, который содержит ваш массив. Если вы вручную добавите перегрузку всех своих функций для обработки этого дополнительного случая, то да, все эти новые функции могут занять некоторое пространство. Если ваша функция мала, у них есть большой шанс быть встроенными и занимать меньше места. Как вы можете видеть из приведенного выше примера, встраивание и постоянное сворачивание могут значительно уменьшить ваш двоичный размер.
В приведенном выше примере, если вы создали второй экземпляр std :: array, будут ли вызовы функций дублировать вызовы функций во флэш-памяти? Хотя оба экземпляра одного типа, int?
Опять это зависит. Если у вас есть много функций, настроенных по размеру массива, оба std::array
а необработанные массивы могут «создавать» разные функции. Но опять же, если они вписаны, нет дубликата, о котором нужно беспокоиться.
Оба будут необработанным массивом и std::array
Вы можете передать указатель на начало массива и передать размер. Если вы найдете это более подходящим для вашего случая, то используйте его, но все еще необработанный массив и std::array
могу сделать это. Для необработанного массива он неявно затухает до указателя и std::array
, вы должны использовать arr.data()
чтобы получить указатель.
Неинициализированный массив не использует flash, за исключением кода, связанного с его использованием.