Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.77/22: Рейтинг темы: голосов - 22, средняя оценка - 4.77
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30

Избыточное копирование объекта при реализации оператора умножения и оператора присваивания

21.03.2016, 18:43. Показов 5235. Ответов 84
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Есть класс работы с матрицами. Есть операция умножения матриц, описанная как оператор класса. В данном коротком примере я просто моделирую ситуацию. Реальное наполнение класса принципиальной разницы не играет, нужно просто понимать, что оператор умножения будет иметь большой код, оператор присваивания в реализации тоже будет не пустым. Эти места в примере засвечены комментариями

C++
#include <cstdio>
 
class Matrix
{
  private:
    // внутренние данные матрицы
 
  public:
    Matrix ();
    Matrix (const Matrix&);
    ~Matrix ();
 
    Matrix& operator=(const Matrix& a);
    Matrix operator*(const Matrix& a) const;
};
 
Matrix::Matrix ()
{
  printf ("Matrix ()\n");
};
 
Matrix::Matrix (const Matrix&)
{
  printf ("Matrix (const Matrix&)\n");
};
 
Matrix::~Matrix ()
{
  printf ("~Matrix ()\n");
};
 
Matrix& Matrix::operator=(const Matrix& a)
{
  printf ("operator=(const Matrix&)\n");
 
  // тут как бы код, копирущий данные из "a" в "this"
 
  return *this;
}
 
Matrix Matrix::operator*(const Matrix& a) const
{
  printf ("operator*(const Matrix&)\n");
 
  Matrix result;
 
  // тут как бы код, умножающий "a" и "this" и записывающий
  // данные в "result"
 
  return result;
}
 
Matrix a, b, c;
 
int main (void)
{
  printf ("------------------\n");
  a = b * c;
  printf ("------------------\n");
}
Исполнение:

Code
$ g++-4.8 t.cc
$ ./a.out
Matrix ()
Matrix ()
Matrix ()
------------------
operator*(const Matrix&)
Matrix ()
operator=(const Matrix&)
~Matrix ()
------------------
~Matrix ()
~Matrix ()
~Matrix ()
Нас интересует только фрагмент печати внутри функции main (т.е. между палками)

Посмотрим на ассемблерный код. Компилирую с опцией -fno-inline, чтобы оставаться с короткой программой и не разводить геморрой по борьбе с inline'ом со стороны компилятора. В общем случае тела операторов класса Matrix будут в отдельном файле и НЕ будут доступны для inline'а. Опцию -fno-exceptions подаю, чтобы было меньше мусора в коде

Code
$ g++ t.cc -O2 -fno-inline -fno-exceptions
$ cat t.s
...
    leal    -9(%ebp), %ebx
    subl    $32, %esp
    movl    %ebx, (%esp)
    movl    $c, 8(%esp)
    movl    $b, 4(%esp)
    call    _ZN6MatrixmlERKS_  <- operator*
 
    subl    $4, %esp
    movl    %ebx, 4(%esp)
    movl    $a, (%esp)
    call    _ZN6MatrixaSERKS_  <- operator=
...
Таким образом мы видим, что построился код, схематично эквивалентный следующему примеру на Си. На всякий случай напоминаю, что с точки зрения семантики C++ выражение "a = b * c" трактуется как "a.operator= (b.operator* (c))"

C
typedef struct { /* внутренности */ } Matrix;
 
extern void operator_assign (Matrix *this, const Matrix *value);
extern void operator_mul (Matrix *result, const Matrix *this, const Matrix *operand2);
 
Matrix a, b, c;
 
int main (void)
{
  Matrix tmp;
 
  operator_mul (&tmp, &b, &c); /* tmp = b.operator* (c) */
  operator_assign (&a, &tmp);  /* a.operator= (tmp) */
}
Мы видим, что в этом коде делается ненужное действие в виде лишнего оператора присваивания. Для умножения матриц эффективный код на Си выглядел бы как

C
operator_mul (&a, &b, &c);
т.е. без заведения промежуточной переменной и дополнительного копирования (которое представляет собой ненужные накладные расходы)

Вопрос. Как правильно написать текст на C++, чтобы получить код, эквивалентный вышеприведённой эффективной реализации на C? Оставаясь при этом в объёме стандарта C++98. Оставляя исходник в понятном и читабельном виде, без использования извращений типа семиэтажных шаблонов и т.п.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
21.03.2016, 18:43
Ответы с готовыми решениями:

Неправильная работа оператора присваивания после работы оператора суммирования
Доброго времени суток. У меня есть класс вектор class TVector {//ewde public: TVector(); //Vector(Vector &amp;v); ...

От каких ошибок страхует Const при перегрузке оператора присваивания
Здравствуйте. Вопрос имею теоретический. В классе A перегружается оператор присваивания, объявление выглядит так: const A operator =...

Почему при перегрузке оператора присваивания, возвращаемое значение не константно?
Почему при перегрузке оператора присваивания, возвращаемое значение - someClass &amp; operator=(const someClass&amp; rhl), а не const...

84
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
21.03.2016, 18:58
Цитата Сообщение от Evg Посмотреть сообщение
Для умножения матриц эффективный код на Си выглядел бы как
operator_mul (&a, &b, &c);
Цитата Сообщение от Evg Посмотреть сообщение
т.е. без заведения промежуточной переменной и дополнительного копирования
у вас в вашем эффективном коде присутствуют три объекта.
то есть имеет место быть промежуточной переменной.

проблема в наличии 3й промежуточной переменной:
http://rextester.com/XMV97300
Matrix ()
Matrix ()
------------------
operator*(const Matrix&)
Matrix ()
------------------
~Matrix ()
~Matrix ()
~Matrix ()
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <cstdio>
 
class Matrix
{
  private:
    // внутренние данные матрицы
 
  public:
    Matrix ();
    Matrix (const Matrix&);
    ~Matrix ();
 
    Matrix& operator=(const Matrix& a);
    Matrix operator*(const Matrix& a) const;
};
 
Matrix::Matrix ()
{
  printf ("Matrix ()\n");
};
 
Matrix::Matrix (const Matrix&)
{
  printf ("Matrix (const Matrix&)\n");
};
 
Matrix::~Matrix ()
{
  printf ("~Matrix ()\n");
};
 
Matrix& Matrix::operator=(const Matrix& a)
{
  printf ("operator=(const Matrix&)\n");
 
  // тут как бы код, копирущий данные из "a" в "this"
 
  return *this;
}
 
Matrix Matrix::operator*(const Matrix& a) const
{
  printf ("operator*(const Matrix&)\n");
 
  Matrix result;
 
  // тут как бы код, умножающий "a" и "this" и записывающий
  // данные в "result"
 
  return result;
}
 
Matrix b, c;
 
int main (void)
{
  printf ("------------------\n");
  Matrix a = b * c;
  printf ("------------------\n");
}
вы в любом случае тратите время на создание 3х объектов.

Цитата Сообщение от Evg Посмотреть сообщение
Как правильно написать текст на C++, чтобы получить код, эквивалентный вышеприведённой эффективной реализации на C? Оставаясь при этом в объёме стандарта C++98.

вот так и писать,как на си: за счет функции, которая принимает источник данных, и объект-назначения
4
 Аватар для meJevin
161 / 153 / 92
Регистрация: 18.11.2015
Сообщений: 677
21.03.2016, 19:07
С такими трудными приколами надо на стак оверфлоу какой-нибудь)
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
21.03.2016, 19:10  [ТС]
Да, в просьбе забыл указать, что такой вариант с конструктором мне так же неинтересен, потому что подразумевает ОБЯЗАТЕЛЬНОЕ конструирование НОВОГО объекта, в то время как хочется записать результат умножения в уже существующий объект без дополнительных накладных расходов. Вариант, когда на каждый чих будет создаваться новая переменная выглядит скорее затычкой, чем реальным решением проблемы

у вас в вашем эффективном коде присутствуют три объекта.
то есть имеет место быть промежуточной переменной
Нету здесь промежуточной переменной. Я привёл пример с одной строкой, чтобы не плодить лишний код. Мы можем переписать код в две строки:

C++
a = b * c;
a = a * a;
В варианте с C++ будет вызвано два оператора умножения и два оператора присваивания, в то время как в эффективном коде на C будет только два оператора умножения. В более общем случае код может состоять из 100 переменных и 200 операций. Заведение новой переменной для каждого действие - это какое-то кособочие, которое попросту ухудшает читабельность и понимаемость программы
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
21.03.2016, 19:20
с++98 можно порешать только за счет эмуляции move
0
 Аватар для Nosey
1379 / 406 / 144
Регистрация: 22.10.2014
Сообщений: 872
21.03.2016, 19:41
Цитата Сообщение от Evg Посмотреть сообщение
подразумевает ОБЯЗАТЕЛЬНОЕ конструирование НОВОГО объекта
Если именно конструирование напрягает, то :
C++
1
2
    a = b;
    a *= c;
Что конечно добавит лишнее присваивание, но это можно обыграть в контексте задачи.

Добавлено через 7 минут
Цитата Сообщение от Evg Посмотреть сообщение
хочется записать результат умножения в уже существующий объект без дополнительных накладных расходов
А если бросится исключение в момент умножения? То получится что С++ будет изменять состояние всех "глобальных" объектов, локальных/новеньких - пожалуйста, а вот ранее созданные - это нини.

Зная вашу дотошность, надеюсь подсказал почему так сделано
1
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
21.03.2016, 20:54  [ТС]
Цитата Сообщение от Nosey Посмотреть сообщение
Если именно конструирование напрягает, то
Скажем так, всяких извращённых способов я могу придумать много. Меня в первую очередь интересует возможность эффективной реализации кода. Пока я не вижу других способов, кроме как написание функции operator_mul, но этот подход уже из разряда Си. Неужто разработчики Си++ сознательно шли на то, что без плясок с бубном тут нельзя будет сделать нормального эффективного решения? Чтобы писать программу просто и естественно, а не заниматься борьбой с языком

Цитата Сообщение от Nosey Посмотреть сообщение
А если бросится исключение в момент умножения? То получится что С++ будет изменять состояние всех "глобальных" объектов, локальных/новеньких - пожалуйста, а вот ранее созданные - это нини
А что, если бросится исключение в момент присваивания? С точки зрения объекта "a" пофигу, бросится исключение в момент "умножения и одновременно присваивания" или просто в момент присваивания.

Я, как афтор кода, знаю, что тут исключений не будет. Конкретно в запуске g++ была опция -fno-exceptions. Но дело ведь не в этом. Дело в том, что с точки зрения базовой концепции Си++ через реализацию операторов класса сия задача эффективно не реализуема, если я правильно понимаю. Я-то думал, что я что-то не так в реализации класса делаю. Судя по всему, тут просто принципиальная невозможность. Пичаль
0
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
21.03.2016, 21:56
Цитата Сообщение от Evg Посмотреть сообщение
Дело в том, что с точки зрения базовой концепции Си++ сия задача эффективно не реализуема
Хочу отметить, что переиспользование одного и того же объекта для разных операций уже не вписывается в концепцию С++. Хотя конечно на абстрактном примере такое грех обсуждать.
Кроме того, с применением move-семантики из С++11 эта задача также реализуется эффективно даже с одним объектом. Ну и проэмулировать move-семантику можно и на С++98, но это уже будет расцениваться как "использование извращений" (хотя реально кода там мало и его достаточно написать один раз и вынести в "библиотеку").
Так что я бы сказал, что задача не вообще не реализуется на С++, а не реализуется с учетом всех твоих конкретных требований и ограничений. Это все-таки разные вещи.
1
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
21.03.2016, 23:27
Цитата Сообщение от Evg Посмотреть сообщение
Неужто разработчики Си++ сознательно шли на то, что без плясок с бубном тут нельзя будет сделать нормального эффективного решения?
у вас вот здесь:

C++
1
2
3
4
5
6
7
8
9
10
11
Matrix Matrix::operator*(const Matrix& a) const
{
  printf ("operator*(const Matrix&)\n");
 
  Matrix result; //<--- создается объект
 
  // тут как бы код, умножающий "a" и "this" и записывающий
  // данные в "result"
 
  return result;
}
программист в коде описал создание объекта.
и объект будет создан, как он и указал.

в случае RVO/NRVO оптимизаций создание объекта
происходит "как бы снаружи" :

C++
1
2
3
4
some obj = foo(); //по факту объект построился не "внутри функции", 
   //а сразу же снаружи,
   // что позволяет избежать копирования
// таким образом получаем эффект "перемещения"
это возможно, потому что стандарт разрешил компиляторам
забивать на возможные эффекты конструктора копии.

но объект в любом случае должен быть построен.
потому что так указал программист.

в случае, если снаружи присвоение,
то выполнить перемещение уже не представляется возможным,
и поэтому RVO/NRVO не сработают.

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

иначе нарушится логика, которую описал программист.
0
 Аватар для Nosey
1379 / 406 / 144
Регистрация: 22.10.2014
Сообщений: 872
22.03.2016, 00:46
Цитата Сообщение от Evg Посмотреть сообщение
Скажем так, всяких извращённых способов я могу придумать много. Меня в первую очередь интересует возможность эффективной реализации кода. Пока я не вижу других способов, кроме как написание функции operator_mul, но этот подход уже из разряда Си.
Ну, *= не совсем извращенный способ, это законный/красивый оператор, который делает ровно то, что вы желаете - изменяет сам объект для которого применяется. Разве не эту цель вы преследуете?

Также не стоит думать, что это все ужасно страшно скажется на производительности, поскольку С++ предполагает частое использование локальных переменных, а не указателей на глобальные, а для изменения непосредственно данной матрицы можно использовать опять же *=, который именно это и предполагает.

Далее, мы видим "лишний" вывод "конструктора и деструктора и т.д.". А что мы ожидали? что компилятор просто так выбросит вывод? Выбросит код, который влияет на внешнюю глобальную переменную? Он не может этого сделать, но это не означает что там где-то в дали на самом деле выделилась память(в данном случае скорее всего означает, но стоит вернуть инлайн, включить lto и все изменится). Если компилятор сможет определить что он может безопасно оптимизировать код - он это сделает в меру своих сил, фишка С++ в том что мы сообщая больше информации и добавляя строгости коду передаем компилятору ответственность за оптимизации. И у него это вполне сносно выходит.
Например :
g++ -O2 -funroll-loops <и черт помнит какие ещё флаги там спрятаны, но без sse и подобного>
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "math/Math.h"
#include <iostream>
 
ru::math::Matrix4 mat1;
ru::math::Matrix4 mat2;
 
ru::math::Matrix4 getMat()
{
    return mat1 * mat2;
}
 
int main()
{
    ru::math::Matrix4 mat = getMat();
    ru::math::Vector3 srcVec { 10, 20, 30 };
    ru::math::Vector3 resVec = mat * srcVec;
    std::cout << resVec.length() << std::endl;
}
И теперь приколы этого кода:
Функция getMap содержит 213 математических ассемблерных инструкций.(mov add mul)
А Функция main - 164 инструкций ( это с учетом всего, в том числе единственный call cout'a)

А в случае:
C++
1
2
3
4
5
6
7
8
9
10
ru::math::Matrix4 mat;
ru::math::Vector3 resVec;
 
int main(void)
{
    mat = getMat();
    ru::math::Vector3 srcVec { 10, 20, 30 };
    resVec = mat * srcVec;
    std::cout << resVec.length() << std::endl;
}
main содержит - 256 инструкций.(1 call cout'a)
Доверяйте компилятору, дайте ему чуть поработать - это идеология плюсов.
И с включенным lto, gcc также отлично инлайнит и оптимизирует между разными единицами трансляции.

Цитата Сообщение от Evg Посмотреть сообщение
А что, если бросится исключение в момент присваивания?
Значит исключение бросится в момент присвоения, именно это и значит , ведь это разные исключения и мы можем их по разному обработать и при соответствующем исключении мы будем знать, что случилось с левым операндом. К списку исключений можно и потокобезопасность добавить, много чего поди можно добавить, но компиляторы пока такое оптимизировать не могут, лет через 100 может и смогут , а пока - таковы правила, но бояться этих правил не надо - см. выше пример.

Цитата Сообщение от Evg Посмотреть сообщение
С точки зрения объекта "a" пофигу, бросится исключение в момент "умножения и одновременно присваивания" или просто в момент присваивания.
Тут да, ему пофигу, поскольку уже началась операция над объектом, и результат зависит только от этой операции.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
22.03.2016, 10:20  [ТС]
Цитата Сообщение от DrOffset Посмотреть сообщение
Хочу отметить, что переиспользование одного и того же объекта для разных операций уже не вписывается в концепцию С++
у вас вот здесь:
...
программист в коде описал создание объекта.
и объект будет создан, как он и указал.
Ну вот про что я и говорю. Развели в языке наноконцепцию, которая никак не дружит с понятием "эффективное программирование"

Цитата Сообщение от DrOffset Посмотреть сообщение
Кроме того, с применением move-семантики из С++11 эта задача также реализуется эффективно даже с одним объектом. Ну и проэмулировать move-семантику можно и на С++98, но это уже будет расцениваться как "использование извращений" (хотя реально кода там мало и его достаточно написать один раз и вынести в "библиотеку").
Можешь показать, что такое move-семантика на C++11 и её эмуляция на C++98? Хотя я так подразумеваю, что это и получится примерно реализация, похожая на operator_mul

Просто вся неэффективность растёт из того, что для реализации операции "a = b * c" на Си я могу описать функцию с тремя параметрами (dst, src1, src2). А с "культурной" реализацией на C++ это будет два оператора mul и assign. Как только оператор mul оказался невидимым для компилятора, у него сразу же пропадает возможность соптимизировать оператор assign

Цитата Сообщение от Nosey Посмотреть сообщение
Ну, *= не совсем извращенный способ, это законный/красивый оператор, который делает ровно то, что вы желаете - изменяет сам объект для которого применяется. Разве не эту цель вы преследуете?
У меня цель такая. Без лишних затрат и ненужных телодвижений выполнить операцию "a = b * c". Последовательность "a = b, a *= c" - это уже само по себе извращение. К тому же оно не избавляет от лишнего копирования, т.е. опять-таки теряем эффективность на ровном месте в борьбе с языком

Цитата Сообщение от Nosey Посмотреть сообщение
Далее, мы видим "лишний" вывод "конструктора и деструктора и т.д."
Вот это меня не пугает ни разу, т.к. конструктор и деструктор будут по сути дела пустыми, а потому они будут описаны в виде inline реализации, которую компилятор нормально соптимизирует

Цитата Сообщение от Nosey Посмотреть сообщение
Доверяйте компилятору, дайте ему чуть поработать - это идеология плюсов
Спасибо, я знаю, как работает компилятор. Именно потому я и пришёл на форум, чтобы спросить, как писать правильно, чтобы не было лишнего кода. Обязательным условием является лишь то, что операторы mul и assign НЕ доступны для инлайна. Пока на свой вопрос я не получил ответа

Цитата Сообщение от Nosey Посмотреть сообщение
Значит исключение бросится в момент присвоения
В случае эффективной реализации в виде operator_mul исключению неоткуда взяться. Поэтому это уже какие-то академические доводы на тему того, что язык спроектирован на все случаи жизни, в то время как на нём я пишу конкретную программу, которую получается написать эффективно только в "некультурном" стиле
0
Неэпический
 Аватар для Croessmah
18149 / 10731 / 2067
Регистрация: 27.09.2012
Сообщений: 27,035
Записей в блоге: 1
22.03.2016, 10:45
Глупенько, конечно, но:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Matrix;
struct pair_mul
{
    pair_mul(const Matrix &a_, const Matrix &b_)
        : a(a_)
        , b(b_)
    {}
    const Matrix &a;
    const Matrix &b;
};
 
//...
    Matrix& Matrix::operator=(const pair_mul& p)
    {
      printf ("operator=(const Pair_mul&)\n");
     
      //умножаем p.a на p.b, результат пихаем в this
      return *this;
    }
//...
    Matrix a, b, c;
    a = pair_mul(b, c);
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
22.03.2016, 10:59  [ТС]
Цитата Сообщение от Croessmah Посмотреть сообщение
Глупенько, конечно, но
Мало того, что это извращение, но при таком раскладе над матрицами можно выполнять только одну операцию, т.к. оператор присваивания всего один
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
22.03.2016, 11:18
Цитата Сообщение от Evg Посмотреть сообщение
В случае эффективной реализации в виде operator_mul исключению неоткуда взяться.
Матрица-приемник имеет размерность 2x2. Результат умножения имеет размерность 3x3. Начали выделять дополнительную память, а она взяла и закончилась. Но содержание приемника мы уже изменили. И тут полетело исключение "начальника, памяти нема".
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
22.03.2016, 13:22  [ТС]
Цитата Сообщение от Renji Посмотреть сообщение
Начали выделять дополнительную память, а она взяла и закончилась
В эффективной реализации не должно быть никаких выделений памяти. Тупо потому, что подо все объекты память уже выделена. Когда в горячем коде начинается портянка из выделений и освобождений памяти, то это уже "академическая" программа, а не "эффективная". К тому же умножение матриц 3x3 и запись результата в матрицу 2x2 - это недопустимое использование интерфейсов (то бишь косяк программиста), который в эффективной реализации должен ломаться через ASSERT, а вовсе не через throw

Добавлено через 6 минут
Цитата Сообщение от Nosey Посмотреть сообщение
Далее, мы видим "лишний" вывод "конструктора и деструктора и т.д."
Цитата Сообщение от Evg Посмотреть сообщение
Вот это меня не пугает ни разу, т.к. конструктор и деструктор будут по сути дела пустыми
Хотя если внутренности матрицы реализовывать через что-то типа std::vector, то тут появятся ещё дополнительные расходы. Ужос
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
22.03.2016, 13:33
Цитата Сообщение от Evg Посмотреть сообщение
В эффективной реализации не должно быть никаких выделений памяти.
Тогда размер матрицы придется задавать на этапе компиляции. И это уже будет не класс Matrix, а класс Matrix3x3 или шаблон Matrix<3,3>. Но да, с такими ограничениями operator* на три аргумента обретает смысл.
Цитата Сообщение от Evg Посмотреть сообщение
К тому же умножение матриц 3x3 и запись результата в матрицу 2x2 - это недопустимое использование интерфейсов (то бишь косяк программиста), который в эффективной реализации должен ломаться через ASSERT, а вовсе не через throw
Ну вас же не удивляет что в std::vector размером в два числа, можно засунуть вектор на десять чисел. Раз приемник неопределенного размера, он должен быть резиновым.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
22.03.2016, 13:36
Цитата Сообщение от Evg Посмотреть сообщение
Развели в языке наноконцепцию, которая никак не дружит с понятием "эффективное программирование"
алгоритм, который был описан программистом - это "наноконцепция" ?
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
22.03.2016, 13:52  [ТС]
Цитата Сообщение от Evg Посмотреть сообщение
В эффективной реализации не должно быть никаких выделений памяти
Цитата Сообщение от Renji Посмотреть сообщение
Тогда размер матрицы придется задавать на этапе компиляции
Разумеется, речь шла о том, что не должно быть выделений памяти в горячих участках кода. Один раз создал экземпляр матрицы с нужным размером и выделением памяти. А потом он просто многократно используется в горячем цикле без каких-либо операций по выделению памяти

Цитата Сообщение от Renji Посмотреть сообщение
Ну вас же не удивляет что в std::vector размером в два числа, можно засунуть вектор на десять чисел
В математике операция умножения над матрицами определена НЕ произвольно. Для этого матрицы должны иметь размеры A*N и N*B. Результатом будет матрица A*B. Не надо путать с действительно резиновым std::vector

Добавлено через 4 минуты
алгоритм, который был описан программистом - это "наноконцепция" ?
Алгоритм - это весьма абстрактное понятие, которое скорее математическое. А вот после этого идёт реализация этого алгоритма в конкретном языке программирования. Вариантов реализации может быть много. Но в общем случае их можно поделить на реализации, написанные "академически" и реализации, написанные "эффективно". Условные 90% тех, кто считает, что знает C++, напишут перемножение матриц именно "академическим" способом, даже не задумавшись об "эффективности". Потому что у многих в подсознании заложено в первую очередь то, что программа должна иметь как можно меньше букв в исходнике, а вовсе не потреблять как можно меньше количество тактов при исполнении
0
 Аватар для Nosey
1379 / 406 / 144
Регистрация: 22.10.2014
Сообщений: 872
22.03.2016, 14:26
Evg,
Можно сравнить С++ подход с ездой на мерседесе, а С подход с ездой от ЗАЗ'а до ферари, в зависимости от уровня разработчика. Если вы на самом деле такой супер разработчик и готовы потратить уйму времени, то скорее всего С подход будет производительнее, но на копейку.(ничего вам не напоминает ASM vs C ? )

Касательно исключений и т.д., я вспомнил, это касается точек следования. Вызов оператора * - это точка следования. -> компилятор не может оставлять сайд эффекты, влияющие на левый операнд присваивания.

И самое главное:
Цитата Сообщение от Evg Посмотреть сообщение
Но в общем случае их можно поделить на реализации, написанные "академически" и реализации, написанные "эффективно".
Приведите примеры, желательно с тестами показывающие разницу, другого способа убедиться что С++ подход в результате не хуже я не знаю. Конечно если no-inline это не критичный для вас флаг, ибо если сломать ноги бегуну, то он загрустит.
0
 Аватар для Kastaneda
5232 / 3205 / 362
Регистрация: 12.12.2009
Сообщений: 8,143
Записей в блоге: 2
22.03.2016, 14:40
Цитата Сообщение от Evg Посмотреть сообщение
Как правильно написать текст на C++, чтобы получить код, эквивалентный вышеприведённой эффективной реализации на C?
Вместо
C++
1
a = b * c;
написать
C++
1
(a = b) *= c;
понятно, что нужно реализовать *=. Практика показывает, что оптимизированный код порой выглядит не совсем логично.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
22.03.2016, 14:40
Помогаю со студенческими работами здесь

Какое значение получит переменная p при выполнении следующего оператора присваивания?
var p: set of 0..9; i, j: integer; Если i=2 и j=5, то какое значение получит переменная p при выполнении следующего оператора...

Ошибка при выполнении оператора присваивания производного класса через указатель на базовый
Здравствуйте, не могу понять из-за чего ошибка. Надо в производном классе объявить оператор сложения и обратится к нему через базовый. ...

Ошибка при реализации перегрузки оператора <<
Добрый день. Прошу помощи. Имеется такой класc. class DList { ... public: ... void Save(std::ofstream &amp;b_out); //...

Ошибка в вводе данных из HTML и переносе их в JavaScript, при выполнении оператора IF или оператора swithc
доброго времени суток, при выполнении одной учебной задачи столкнулся с проблемой: при введении любого значения в поле, код выдает только...

Переопределение оператора присваивания
Имеется такой простой класс: class TClass { private: float* A; int N; public: TClass(int _N) ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем. . . .
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru