Оптимизация выполнения большого числа функций

Стоит такая задача.
Есть два параметра (пусть arg1 и arg2). Каждый из них может принимать одно из значений:
arg1 = 0, 1, 2, …, N; arg2 = 0, 1, 2, …, M.
N и M - константы, известные еще до компиляции кода, в районе 10-12.
В зависимости от значений обоих параметров должна выполняться определенная функция. Функции очень короткие, всего 1-3 действия.
Так вот хочу посоветоваться, как написать наиболее оптимальный код. У меня есть два предложения.

  1. Создаем двухмерный массив указателей на функции и вызываем их исходя из значений параметров:
#define  N   4
#define  M  3

typedef   void(*DoFunc)();

void func1(),  func2(), func3();

DoFunc doFunc[N][M] = {
                          func1,     NULL,   NULL,   
                          NULL,   func2,   NULL,   
                          NULL,   func3,   NULL,   
                          NULL,   NULL,   NULL    
                      };

void  doProc(uint8_t arg1, uint8_t arg2) {  
    ProcFunc pf;
    if (!(pf = doFunc[arg1][arg2])) return;
    pf();
}

void func1() {
// Действия 1
}

void func2() {
// Действия 2
}

void func3() {
// Действия 3
}
  1. Решаем задачу в лоб:
void doProc(uint8_t arg1, uint8_t arg2) {
    switch(arg1) {
        case 0:
            switch(arg2) {
                case 0: // Действия
                break; 
                case 1:// Действия
                break;
                ...
            }
        break;
        ...
    }
}

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

Строка 16 - ProcFunc pf; читать DoFunc pf;

С константными аргументами, полагаю, оптимизатор разберется и повыкидывает из функций лишнее.

А если уж на него не надеяться, то, говорят, constexpr-функции помогают.

цифры покажи и сам решишь - стоит оно того или нет.

1 лайк

Ежели все известно в момент компиляции, то сделать шаблон функции с двумя целочисленными параметрами и написать специализацию на каждый набор. Будет само выбираться, плюс к тому в конечный код попадут только функции, вызываемые в этом коде

1 лайк

Собственно, это примерно тот же вариант 2. Тут прелесть еще в том, что компилятор сам оценит, что короче - делать вызов функции или вставить ее исполнение непосредственно в код, типа “inline void func1()”. А в первом случае все функции будут вызываться, хотя и в одном месте, но стек будет обрабатываться для каждой функции.
Вот и делема! Чего-то склоняюсь к мысле потратить время и проверить все это опытным путем.

Что-то у меня сомнение - вы вообще поняли, что я предлагаю? С шаблонами работали?

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

1 лайк

А если их пятьсот?

А как он поймет какую? Они все, с точки зрения компилятора, в данном случае одинаковые.
Пример можно?

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

Если только вечером. На телефоне код трудно набирать.
Поищите пока сами - специализация шаблона функции

Их будет изначально около 50, но может произойти, что ряд значений одного из параметров измениться. Для первого варианта - это просто исправить.
Вот ни как не пойму, как в данном случае можно использовать шаблоны… Ну, вот простой пример функций, допустим их всего 4:

extern int A, B, C;

void func1() {
    A = B;
}

void func2() {
    C = B + A;
    B = A - 10;
}

void func3() {
    A = B = C = 0;
}

void func1() {
    В = -B;
}

Можно увидеть использование шаблона для такого случая?

ты каркулятор штоли изобретаешь?

Колесо же …

Типа того, только китайский. Я просто не пойму как в моем случае можно применить шаблоны. Функции будут совсем разные, хотя достаточно простые, примерного уровня “2+2”.
Если интересно расскажу.
Есть устройство, которое передает нам данные с некоего датчика (arg1 = 0…N). Есть некое предопределенное перманентное состояние устройства управления процессом (arg2 = 0…M). На основании этих данных необходимо внести изменения в процесс работы устройства.
Например. Мы с утра следуем к магазину на Ленина к винному магазину чтобы похмеляться (arg1). Но дойдя, видим, что он закрыт на учет (arg2). Мы принимаем решение следовать до магазина на улице Горького (изменение маршрута - func1()). Но магазин мог не только быть на учете, он мог просто еще не открыться. Придется подождать - func2(). И т.д… А вы, каркулятор!!!

template<int A, int B>
void myFunc() {
  Serial.println("Заглушка");
}
template<>
void myFunc<3,2>() {
  Serial.println("3-2 called! ");
}
template<>
void myFunc<1,2>() {
  Serial.println("1-2 called! ");
}
template<>
void myFunc<1,1>() {
  Serial.println("1-1 called! ");
}
template<>
void myFunc<3,3>() {
  Serial.println("3-3 called! ");
}
template<>
void myFunc<3,1>() {
  Serial.println("3-1 called! ");
}




void setup() {
  Serial.begin(115200);

  myFunc<3,2>();
  myFunc<1,2>();
  myFunc<1,1>();
  

}

void loop() {
  // put your main code here, to run repeatedly:

}

Вот тебе пример того, про что МММ написал. Но таблица точно уместнее, твой первый вариант.
Потому что то, что в угловых скобках, должно быть известно на момент компиляции.

ps: ачипятку поправил

Вот здесь и засада. Значения меняются.

Ну так и пиши массив! даже 10 на 10 на 32 битном контроллере это 400 байт. Какое они имеют значение? Или ты используешь AVR? Выброси его.
Что за желание ужиматься в 2 К памяти, при той же цене? В прошлый век возвращаемся? Тогда давай воловьи жилы выделывать и бить дичь из лука!

Тут вы правы.
И кстати, по вашему примеру, вот тоже самое но без шаблонов:

void myFunc() {
  Serial.println("Заглушка");
}
void myFunc32() {
  Serial.println("3-2 called! ");
}
void myFunc12() {
  Serial.println("1-2 called! ");
}
void myFunc11() {
  Serial.println("1-1 called! ");
}
void myFunc33() {
  Serial.println("3-3 called! ");
}
void myFunc31() {
  Serial.println("3-1 called! ");
}

void setup() {
  Serial.begin(115200);

  myFunc32();
  myFunc12();
  myFunc11();
  
}

Опять нифига ты не понял :frowning:

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