Что мешает не вычислять size() каждую итерацию цикла?
От: Zhendos  
Дата: 30.04.21 12:57
Оценка:
Почему код ниже превращается в ассебмлер, где каждую итерацию цикла
"size" перевычисляется? Грубо говоря "foo" получает "MyVec<int> *",
почему компилятор считает что "MyVec<MyVec<int>>" может из-за этого поменяться?

#include <cstddef>

template<typename T>
struct MyVec {
    T *begin;
    T *end;

    constexpr size_t size() const noexcept { return end - begin; }
    const T& operator[](size_t i) const noexcept { return begin[i]; }
};

void foo(MyVec<int> const&);

void indexed(MyVec<MyVec<int>> const& in) {
    for (size_t idx = 0; idx < in.size(); ++idx) {
        foo(in[idx]);
    }
}


Ссылка на godbolt
Re: Что мешает не вычислять size() каждую итерацию цикла?
От: Videoman Россия https://hts.tv/
Дата: 30.04.21 13:28
Оценка: 6 (1)
Здравствуйте, Zhendos, Вы писали:

Z>Почему код ниже превращается в ассебмлер, где каждую итерацию цикла

Z>"size" перевычисляется? Грубо говоря "foo" получает "MyVec<int> *",
Z>почему компилятор считает что "MyVec<MyVec<int>>" может из-за этого поменяться?

Мешает передача в функцию по ссылке, скорее всего, т.к. компилятор думает что снаружи вектор может поменяться. Вот так — уже не думает.
Re: Что мешает не вычислять size() каждую итерацию цикла?
От: andyp  
Дата: 30.04.21 13:29
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>Почему код ниже превращается в ассебмлер, где каждую итерацию цикла

Z>"size" перевычисляется? Грубо говоря "foo" получает "MyVec<int> *",
Z>почему компилятор считает что "MyVec<MyVec<int>>" может из-за этого поменяться?

То, что foo обещает не менять один из элементов in вовсе не значит, что она обещает не менять то, на что ссылается in, например получив внутри другую, уже неконстантную ссылку на этот же объект. Компилятор этого не видит и предполагает худшее.
Re: Что мешает не вычислять size() каждую итерацию цикла?
От: rg45 СССР  
Дата: 30.04.21 13:32
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>почему компилятор считает что "MyVec<MyVec<int>>" может из-за этого поменяться?


Потому, что константность ссылки, по которой вектор передается в foo, не гарантирует его неизменяемости. Да, взлом константности не есть хорошо с точки зрения дизайна, но программа-то остается well-formed, тем не менее. А значит, компилер обязан корректно обработать и этот случай.

Да и сколько там тех вычислений. Два mov-а и один sub. Неужто чувствительно?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 30.04.2021 13:34 rg45 . Предыдущая версия . Еще …
Отредактировано 30.04.2021 13:33 rg45 . Предыдущая версия .
Re[2]: Что мешает не вычислять size() каждую итерацию цикла?
От: Zhendos  
Дата: 30.04.21 13:38
Оценка:
Здравствуйте, rg45, Вы писали:

R>Потому, что константность ссылки, по которой вектор передается в foo, не гарантирует его неизменяемости. Да, взлом константности не есть хорошо с точки зрения дизайна, но программа-то остается well-formed, тем не менее. А значит, компилер обязан корректно обработать и этот случай.


Ну и пусть "foo" поменяет элемент типа "MyVec<int>" полностью,
причем здесь MyVec<MyVec<int>>, ссылка ведь передается на "MyVec<int>",
главный то вектор к этому каким боком и как изменение его элемента может изменить размер самого вектора?
Re[3]: Что мешает не вычислять size() каждую итерацию цикла?
От: rg45 СССР  
Дата: 30.04.21 13:40
Оценка: +1
Здравствуйте, Zhendos, Вы писали:

Z>Ну и пусть "foo" поменяет элемент типа "MyVec<int>" полностью,

Z>причем здесь MyVec<MyVec<int>>, ссылка ведь передается на "MyVec<int>",
Z>главный то вектор к этому каким боком и как изменение его элемента может изменить размер самого вектора?

Внутри foo может выполняться вставка или удаление, что изменит размер вектора. Да что там вставка и удаление, полнотью может измениться все его содержимое. И если компилер в точке обращения к foo не видит ее определения, он не может исключать такую возможность.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 30.04.2021 13:44 rg45 . Предыдущая версия . Еще …
Отредактировано 30.04.2021 13:42 rg45 . Предыдущая версия .
Отредактировано 30.04.2021 13:40 rg45 . Предыдущая версия .
Re[2]: Что мешает не вычислять size() каждую итерацию цикла?
От: Zhendos  
Дата: 30.04.21 13:40
Оценка:
Здравствуйте, andyp, Вы писали:

A>Здравствуйте, Zhendos, Вы писали:


A>То, что foo обещает не менять один из элементов in вовсе не значит, что она обещает не менять то, на что ссылается in, например получив внутри другую, уже неконстантную ссылку на этот же объект. Компилятор этого не видит и предполагает худшее.


Так пусть foo меняет что угодно в пределах элемента и полностью игнорирует const,
на как изменение элемента влияет на размер основного вектора, почему-то он-то может поменяеться?
Re[4]: Что мешает не вычислять size() каждую итерацию цикла?
От: Zhendos  
Дата: 30.04.21 13:54
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, Zhendos, Вы писали:


R>Внутри foo может выполняться вставка или удаление, что изменит размер вектора. Да что там вставка и удаление, полнотью может измениться все его содержимое. И если компилер в точке обращения к foo не видит ее определения, он не может исключать такую возможность.


Да пусть там что угодно выполняется, как это на основной вектор та влияет?
У меня в пример MyVec<MyVec<int>>, а в foo передается просто MyVec<int>,
пусть MyVec<int> как угодно поменяется, вычисляется то ведь MyVec<MyVec<int>>::size,
то есть длина совершенно другого вектора.
Re[3]: Что мешает не вычислять size() каждую итерацию цикла?
От: Videoman Россия https://hts.tv/
Дата: 30.04.21 14:02
Оценка: 4 (1) +4
Здравствуйте, Zhendos, Вы писали:

Z>Так пусть foo меняет что угодно в пределах элемента и полностью игнорирует const,

Z>на как изменение элемента влияет на размер основного вектора, почему-то он-то может поменяеться?

Вот простейший пример, который иллюстрирует что может быть.
#include <cstddef>

template<typename T>
struct MyVec {
    T *begin;
    T *end;

    constexpr size_t size() const noexcept { return end - begin; }
    const T& operator[](size_t i) const noexcept { return begin[i]; }
};

MyVec<MyVec<int>> outter;

void foo(MyVec<int> const& in)
{
    outter.begin = nullptr; // Меняем контейнер !!!
    outter.end = nullptr;   // Меняем контейнер !!!
}

void indexed(MyVec<MyVec<int>> const& in) {

    for (size_t idx = 0; idx < in.size(); ++idx) {
        foo(in[idx]);
    }
}

int main() 
{
    indexed(outter);

    return 0;
}
Re[3]: Что мешает не вычислять size() каждую итерацию цикла?
От: andyp  
Дата: 30.04.21 14:28
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>Так пусть foo меняет что угодно в пределах элемента и полностью игнорирует const,

Z>на как изменение элемента влияет на размер основного вектора, почему-то он-то может поменяеться?

Что значит пусть? Вообще-то foo может менять всё, кроме этого элемента, если он const Никто ж не может обещать, что она будет использовать только то, с чем ты ее вызвал.
Re[5]: Что мешает не вычислять size() каждую итерацию цикла?
От: rg45 СССР  
Дата: 30.04.21 14:30
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>У меня в пример MyVec<MyVec<int>>, а в foo передается просто MyVec<int>,


Фу, блин, только сейчас досмотрелся, что у тебя там вектор в векторе. Виноват, не досмотрелся.

Но даже таком случае у компилятора нет достаточных оснований считать, что внешний вектор не изменяется внутри foo. А раз так, то и оптимизацию вычислений он применить не может.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Что мешает не вычислять size() каждую итерацию цикла?
От: Vamp Россия  
Дата: 30.04.21 14:31
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>Да пусть там что угодно выполняется, как это на основной вектор та влияет?


Да не в этом дело. У тебя функция `foo` может сделать с твоим вектором что угодно в принципе. Ей совершенно необязательно для этого использовать параметр, переданный по константной ссылке. В простейшем случае, оригинальный вектор может быть глобальным объектом, который foo меняет в свое удовольствие напрямую, игнорируя аргумент.
Да здравствует мыло душистое и веревка пушистая.
Re[4]: Что мешает не вычислять size() каждую итерацию цикла?
От: Zhendos  
Дата: 30.04.21 14:41
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Вот простейший пример, который иллюстрирует что может быть.[ccode]


Спасибо. Пометил функцию как неработающую с глобальными переменными и компилятор тут же начал генерировать
нормальнй код.
Re: Что мешает не вычислять size() каждую итерацию цикла?
От: LaptevVV Россия  
Дата: 01.05.21 06:49
Оценка:
Z>
Z>#include <cstddef>

Z>template<typename T>
Z>struct MyVec {
Z>    T *begin;
Z>    T *end;

Z>    constexpr size_t size() const noexcept { return end - begin; }
Z>    const T& operator[](size_t i) const noexcept { return begin[i]; }
Z>};
Z>

У тебя указатели begin, end — не константные.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: Что мешает не вычислять size() каждую итерацию цикла?
От: Pzz Россия https://github.com/alexpevzner
Дата: 01.05.21 10:11
Оценка: +1
Здравствуйте, Zhendos, Вы писали:

Z>Почему код ниже превращается в ассебмлер, где каждую итерацию цикла

Z>"size" перевычисляется? Грубо говоря "foo" получает "MyVec<int> *",
Z>почему компилятор считает что "MyVec<MyVec<int>>" может из-за этого поменяться?

Потому, что foo потенциально может добраться до вектора по глобальной ссылке, которую компилятор не видит, но подозревает, что она есть.
Re: Что мешает не вычислять size() каждую итерацию цикла?
От: Типичный Теолог  
Дата: 01.05.21 16:28
Оценка: :)
Что мешает использовать итератор?

for (auto & i: in) {
foo(i);
}


Z> почему компилятор считает что "MyVec<MyVec<int>>" может из-за этого поменяться?


Потому что может поменяться в другом потоке.
Я/Мы Иностранный агент
Отредактировано 01.05.2021 16:29 Типичный Теолог . Предыдущая версия .
Re[2]: Что мешает не вычислять size() каждую итерацию цикла?
От: andyp  
Дата: 01.05.21 16:38
Оценка:
Здравствуйте, Типичный Теолог, Вы писали:

ТТ>Потому что может поменяться в другом потоке.


Где ж ты такое вычитал
Re[5]: Что мешает не вычислять size() каждую итерацию цикла?
От: koenjihyakkei Россия  
Дата: 04.05.21 16:02
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>Спасибо. Пометил функцию как неработающую с глобальными переменными и компилятор тут же начал генерировать

Z>нормальнй код.

А это как?
Re[6]: Что мешает не вычислять size() каждую итерацию цикла?
От: Zhendos  
Дата: 04.05.21 22:58
Оценка: 39 (2)
Здравствуйте, koenjihyakkei, Вы писали:

K>Здравствуйте, Zhendos, Вы писали:


Z>>Спасибо. Пометил функцию как неработающую с глобальными переменными и компилятор тут же начал генерировать

Z>>нормальнй код.

K>А это как?


В gcc и clang — __attribute__((const))
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.