powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / изменить адрес ссылки
63 сообщений из 63, показаны все 3 страниц
изменить адрес ссылки
    #38390132
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Известно, что обращение к переменной по указателю type* осуществляется в два шага: считывание адреса из указателя, переход по адресу. В то же время ссылка type& содержит прямой адрес на значение переменной в памяти, поэтому обращение по ссылке занимает на один шаг меньше. При выполнении высокопроизводительных операций эта разница ощутима. Возникает вопрос - как динамически изменить адрес ссылки, если заранее неизвестно, где будет расположена связанная со ссылкой переменная.

Для наглядности рассмотрим пример:

Код: plaintext
1.
2.
3.
double d1 = 1., d2 = 2.; // две переменные
double* d = &d1; // указатель на первую переменную
d = &d2; // указатель на вторую переменную



Аналогичная операция со ссылками:

Код: plaintext
1.
2.
3.
double d1 = 1., d2 = 2.;
double& d = d1;
&d = &d2; // ошибка! как это сделать правильно?



Возможно, возникнет вопрос - зачем это все нужно. Поясню, в моей задаче есть массив структур, содержащих статические (в смысле не изменяемые) и динамические переменные. Динамические переменные нужно часто обнулять и в некоторые из них загонять новые значения. Делать это нужно много миллионов раз, поэтому было бы выгодно вместо переменных использовать ссылки на элементы массива, который можно эффективно обнулять с помощью memset, а заполнять из кеша с помощью memcopy. Это гораздо быстрее циклического перебора структур с обращением к каждой динамической переменной. Беда в том, что количество структур заранее неизвестно, поэтому неизвестен размер массива, а значит на момент создания структуры нам нечем проинициализировать ссылку и ее придется инициализировать позже.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390136
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsИзвестно, что обращение к переменной по указателю type* осуществляется в два шага: считывание адреса из указателя, переход по адресу. В то же время ссылка type& содержит прямой адрес на значение переменной в памяти, поэтому обращение по ссылке занимает на один шаг меньше. При выполнении высокопроизводительных операций эта разница ощутима
Это не так.
На уровне машинного кода реализация указателя и ссылки одинаковая, поэтому никакой разницы в скорости у них нет. (Где вы вообще про такое прочли?)

Соответственно, необходимость изменения ссылок надумана. Используйте указатели.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390144
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly MoskovskyandreybsИзвестно, что обращение к переменной по указателю type* осуществляется в два шага: считывание адреса из указателя, переход по адресу. В то же время ссылка type& содержит прямой адрес на значение переменной в памяти, поэтому обращение по ссылке занимает на один шаг меньше. При выполнении высокопроизводительных операций эта разница ощутима
Это не так.
На уровне машинного кода реализация указателя и ссылки одинаковая, поэтому никакой разницы в скорости у них нет. (Где вы вообще про такое прочли?)

Соответственно, необходимость изменения ссылок надумана. Используйте указатели.

Читал на stackoverflow.com. Эксперименты показывают, что скорость отличается. Возможно, это из-за двойного преобразования. Ведь если есть double& a и double b, то вычислить сумму можно напрямую: a+b, а если есть double* a и double b, то сумма требует на одну операцию больше: *a+b (добавился переход по ссылке *). Разве не так?
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390148
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly MoskovskyНа уровне машинного кода реализация указателя и ссылки одинаковая, поэтому никакой разницы в скорости у них нет.

Возможно, Вы правы. Я провел еще эксперимент - сравнил скорость работы указателя и ссылки. Разницы не увидел - это может косвенно подтверждать, что реализация на уровне машинного кода одинаковая. Вот что меня действительно удивило - это что доступ по ссылке работает медленнее, чем прямой доступ к переменной. Видимо, ссылка на уровне машинного кода сама реализуется, как переменная с адресом, а не константа. Непонятно только, если это не константа, то зачем было запрещать изменять ее адрес bydesign.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390150
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs,

Нет, не так. Как - я выше уже писал: ссылки и указатели- это одно и тоже.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
void f_ref(int& v)
{
    v += 5;
}

void f_ptr(int* v)
{
    *v += 5;
}


Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
    .p2align 4,,15
    .globl  _Z5f_refRi
    .type   _Z5f_refRi, @function
_Z5f_refRi:
.LFB0:
    .cfi_startproc
    addl    $5, (%rdi)
    ret
    .cfi_endproc
.LFE0:
    .size   _Z5f_refRi, .-_Z5f_refRi
    .p2align 4,,15
    .globl  _Z5f_ptrPi
    .type   _Z5f_ptrPi, @function
_Z5f_ptrPi:
.LFB1:
    .cfi_startproc
    addl    $5, (%rdi) 
    ret
    .cfi_endproc



Теперь давайте ссылку на stackoverflow где это написано, и код который показывает отличия в скорости.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390152
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsНепонятно только, если это не константа, то зачем было запрещать изменять ее адрес bydesign
Потому что на ссылках висят еще и разные семантические правила, которые применяются в compile-time.
Если не в даваться в детали, то ссылку нельзя менять по тем же причинам, по которым нельзя поменять адрес объявленной переменной.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390158
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly MoskovskyЕсли не в даваться в детали, то ссылку нельзя менять по тем же причинам, по которым нельзя поменять адрес объявленной переменной.

Чтобы не приводить код к "unsafe" из-за возможных утечек памяти?
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390181
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs,

Ну не из-за утечек, но да, для безопасности кода.
Также как когда мы пишем const int i = 3, то в программе мы менять значение не можем, хотя в машинном коде память доступна для записи.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390227
BagaBaga
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs,
ссылки (если угодно - автоматически разименуемые указатели) дают выигрыш только на больших структурах данных, а вот на "плоских" - нет. Просто потому, что размер этих данных (double, int, etc..) сравним с размером указателя, и тогда что передавать по ссылке, что по значению - всё равно. Для "плоских типов" передавать что-то по ссылке (точно так же, как и по указателю) нужно только в том случае, если "получатель" ссылки должен менять значения, и они должны "отражаться в оригинале" (при передаче по копии такие изменения в "источник" не вернутся). Для больших типов (вроде вектров, списков и т.д) экономия происходит за счёт избегания копирования большого количества элементов контейнера.

Вот простой пример:
int i = 10;
int ii = 1000;
int& j =i;
cout << i << endl;
j = 100;
cout << i << endl;
j = ii;
cout << i << endl;

вывод этого кода будет
10
100
1000

А вот переопределить ссылку (чтобы они сначала указывала на одну переменную, а с какого-то места - на другую) - не получится. Но вы можете для себя написать класс-обрёртку, которая будет играть эту роль. Вот только нужно ли? Похоже, что вам надо сначала сформулировать задачу, а уже потом задумываться об эффективности её решения.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390264
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
BagaBagaА вот переопределить ссылку (чтобы они сначала указывала на одну переменную, а с какого-то места - на другую) - не получится. Но вы можете для себя написать класс-обрёртку, которая будет играть эту роль. Вот только нужно ли?

Нужно. В итоге я так и сделал - шаблон class var<T>. Синтаксически с ним можно работать как со ссылкой, но при этом можно менять адрес его указателя. Так динамические переменные заменены с double на указатели vdouble (синоним var<double>). Указатель содержит адрес элемента массива, через который производятся массовые операции изменения значений динамических переменных.

Есть только одно неудобство. Например, для double есть операция +=, а для bool ее нет. Поэтому перегрузку оператора += не получается включить в состав класса var<T>. Прочие операции +,-,*,/ и т.п. работают автоматически, т.к. перегружены операторы прямого-обратного преобразования:
T& operator=(TT &src) // TT - любой тип будет преобразован к T и извлечено значение в указатель
operator T() // возвращается значение типа T&
operator TT() // возвращается значение типа T&
Но для операции += этого недостаточно. Например, var<double> += double не предусмотрен, нужна перегрузка оператора += для var<T>. Интересно, можно ли это как-то обойти, явно не перегружая операторы ++,--,+=,-=
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390287
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsЕсть только одно неудобство. Например, для double есть операция +=, а для bool ее нет. Поэтому перегрузку оператора += не получается включить в состав класса var<T>.
А можете продемонстрировать на примере кода, что именно не получается?

И кстати, зачем вам обертка для имитации ссылок, если вы можете в любой момент из указателя сделать ссылку?

Код: plaintext
1.
2.
3.
int* ptr = ...;
int& ref = *ptr;
// дальше работаем со ссылкой
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390357
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs,

Зачем городить какие-то классы вместо указателей, а не пользоваться самими указателями?
Кроме этого, умных указателей уже написано достаточно много.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390427
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
авторАналогичная операция со ссылками:

Код: plaintext
1.
2.
3.
double d1 = 1., d2 = 2.;
double& d = d1;
&d = &d2; // ошибка! как это сделать правильно?



Никак. Ссылка -- это константный разименованный указатель. Он НЕ МЕНЯЕТСЯ никогда.
Это одно из основных отличий ссылки от указателя. Указатель может указывать на разные объекты в течении своей жизни, ссылка -- не может, после инициализации она указывает всегда на один и тот же объект.


авторВозможно, возникнет вопрос - зачем это все нужно. Поясню, в моей задаче есть массив структур, содержащих статические (в смысле не изменяемые) и динамические переменные. Динамические переменные нужно часто обнулять и в некоторые из них загонять новые значения. Делать это нужно много миллионов раз, поэтому было бы выгодно вместо переменных использовать ссылки на элементы массива, который можно эффективно обнулять с помощью memset, а заполнять из кеша с помощью memcopy. Это гораздо быстрее циклического перебора структур с обращением к каждой динамической переменной. Беда в том, что количество структур заранее неизвестно, поэтому неизвестен размер массива, а значит на момент создания структуры нам нечем проинициализировать ссылку и ее придется инициализировать позже.

Я мало что понял из этого потока сознания.

Используй указатели значит, они ничем не хуже ссылок.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390475
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly MoskovskyА можете продемонстрировать на примере кода, что именно не получается?


Вот класс-обертка:

Код: plaintext
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.
	template <typename T>
	class var
	{
	private:

		T* _ptr;

	public:

		var() 
		{
			_ptr = NULL;
		}

		var(T &src) 
		{
			_ptr = &src;
		}

		inline T& operator=(T *src)
		{
			_ptr = src;
			return *_ptr;
		}

		template <typename TT> inline T& operator=(const TT &src)
		{
			assert(_ptr!=NULL);
			*_ptr = (T)src;
			return *_ptr;
		}

		inline operator T() 
		{
			assert(_ptr!=NULL);
			return *_ptr;
		}

		template <typename TT> inline operator TT() 
		{
			assert(_ptr!=NULL);
			return *_ptr;
		}

	};



Вот пример использования:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
double d1=1, d2=2;
_variant_t v1=3, v2=4;
var<double> d=d2;

v1 = d; // ok
d = 10;  // ok
d = d+d1; // ok
d += d1; // не работает, т.к. нет перегрузки оператора += для класса var



Anatoly MoskovskyИ кстати, зачем вам обертка для имитации ссылок, если вы можете в любой момент из указателя сделать ссылку?


Просто пока программа не написана полностью, а работа со структурами, члены которых сейчас заменены на такие "указатели" выполняется уже во всех модулях. Поэтому переколбашивать все модули, ставя перед всеми обращениями к указателю * не удобно. Может потом, когда структура программы устаканится, я уберу класс-обертку, но пока им удобно польльзоваться и он не влияет на производительность (в сравнении со ссылками).
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390479
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
MasterZivЯ мало что понял из этого потока сознания.


Это, наверное, от того, что что тратишь много энергии на критику. Ты же считаешь, что я даже класс от объекта отличить не могу... Ну и не трать свое время на общение с неучами, как нибудь сами разберемся.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390490
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
double d1=1, d2=2;
_variant_t v1=3, v2=4;
var<double> d=d2;

v1 = d; // ok
d = 10;  // ok
d = d+d1; // ok
d += d1; // не работает, т.к. нет перегрузки оператора += для класса var



А ну тут все правильно. Так как для операции d+d1 производится поиск возможных преобразований типа операндов к единому типу и для var такое преобразование есть: operator T, то все работает.
А для += такого поиска преобразования не производится.
Это связано с тем что операция + для встроенных типов определена как если бы она была свободной функцией, а операция += - как если бы она была методом (в этом случае потребовалось бы 2 последовательных неявных преобразования, а это запрещено правилами С++).

Таким образом, вам действительно нужно в вашем шаблоне определить оператор += и прочие.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390495
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly MoskovskyТаким образом, вам действительно нужно в вашем шаблоне определить оператор += и прочие.

А не будет ошибок при использовании типов, где эта операция не определена, например, var<bool>?
Я пока не проверял, просто заменил в одном месте программы x += y на x=x+y, чтобы убрать ошибку.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390497
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я то думал что вы определили оператор += в шаблоне и для инстанцирования bool это не работало (по вашим словам из-за отсутствия += у bool).
Так вот, для bool должно работать (если конечно вы не вызываете += для var<bool>)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390499
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsА не будет ошибок при использовании типов, где эта операция не определена, например, var<bool>?
Не будет ошибок, т.к. тела шаблонов проверяются только при их явном вызове.
Это относится как телам функций в шаблонных классах(а так же и другим объявлениям внутри класса), так и телам функций-шаблонов.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390500
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsMasterZivЯ мало что понял из этого потока сознания.


Это, наверное, от того, что что тратишь много энергии на критику. Ты же считаешь, что я даже класс от объекта отличить не могу... Ну и не трать свое время на общение с неучами, как нибудь сами разберемся.

Спасибо, что предупредил, не буду и тратить....
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390501
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs,

Кстати, а почему вы решили что += не определена для bool? :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390503
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
inline T& operator=(T *src)
{
_ptr = src;
return *_ptr;
}

template <typename TT> inline T& operator=(const TT &src)
{
assert(_ptr!=NULL);
*_ptr = (T)src;
return *_ptr;
}



Плохой стиль.
Ты одновременно позиционируешь ЭТО
И как указатель, и как ссылку.
Плохо.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390510
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly Moskovsky,

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

Тут вот еще вопрос вчера возник, с которым я пока не разобрался. Мне непонятно, почему одновременно приходится прописывать:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
		inline operator T() 
		{
			assert(_ptr!=NULL);
			return *_ptr;
		}

		template <typename TT> inline operator TT() 
		{
			assert(_ptr!=NULL);
			return *_ptr;
		}



Если убрать Т(), то перестают работать выражения вида {double} += {var<double>}, а если убрать ТТ(), то перестают работать вызовы функций F(_variant_t arg), где в качестве arg подставляется var<double>. По идее ТТ() должен полностью перекрывать Т(), но на деле как будто ТТ() рассматривается для всех типов, кроме Т (т.е. double в var<double>). При этом в аналогичной ситуации оператор =(ТТ &src) перекрывает оператор =(Т &src).

Собственно мой вопрос в в том, как устроен алгоритм выбора подходящего шаблона? В частности, для конструкции
template <typename TT> inline operator TT() внутри класса template <typename T> class var
для реализации шаблона var<double> тип ТТ может рассматриваться, как double или нет (т.е. ТТ - это любой тип, кроме Т=double)?
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390513
BagaBaga
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
[quot andreybs]BagaBagaЕсть только одно неудобство. Например, для double есть операция +=, а для bool ее нет. Поэтому перегрузку оператора += не получается включить в состав класса var<T>.

Если речь идёт о вашей обёртке, то я принципиально не вижу проблемы. Например, вот такой код
Код: plaintext
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.
#include <iostream>

using namespace std;

class A{
    private:
    double* ptr;
    public:
    A():ptr{nullptr}{};
    A(const double dd):ptr{new double(dd)}{};
    A& operator+=(const double d){
        *ptr +=d;
        return *this;};
    double tell()const{return *ptr;}
};


int main()
{
   double d1{1};
   A a{10};
   a+=d1;
   cout << a.tell() <<"Hello World" << endl;  
   return 0;
}


выводит
Код: plaintext
1.
2.
3.
$demo

11Hello World


Я намеренно не использовал шаблоны, чтобы за "деревьями" не "потерять лес". Переписать это на шаблоны нет проблем. Там, конечно, нужен будет не только оператор, принимающий double (в вашем случае - тип-параметр), но и оператор, принимающий в качестве входа "такую же обёртку" (а также аналогичный конструктор), но в данном случае это суть не изменит, а строк добавит. Для особых случаев (для тех типов, для которых операция *ptr +=d не определена) вы можете сделать специализацию шаблона и задать то поведение, которое нужно в соответствии с вашей задачей (если такая операция просто недопустима, то можно породить исключение, правда, мне это кажется нелогичным - хотеть оператор для "общего" случая и не иметь понятия о желаемом его поведении).

Но я всё равно не понимаю вашей задачи (да она и не озвучена). Использование стандартного контейнера предполагает использование предоставливаемых им штатных средств. Знание того, что вектор хранит свои структуры в непрерывном "куске памяти" полезно при обеспечении совместимости с С-кодом, а вот в других случаях - это верный способ "выстрелить себе в ногу". В отличии от memset, контейнер при создании каждого элемента вызывает его конструктор, ну, и деструктор при выходе из области жизни. Простое же копирование допустимо только для "плоских" типов, что идёт в разрез со "сложными непонятно где определёнными типами".

Приведу простой пример:
создание простого массива (через new) и его последующая инициализация с memset по сравнению с vector<int>v(elem_number, 0) давало выигрыш менее 0.5%.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390514
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly Moskovskyandreybs,

Кстати, а почему вы решили что += не определена для bool? :)

У меня компилятор предупреждение выдает...
warning C4804: +=: небезопасное использование типа "bool" в операции

Полагаю, он bool в int конвертит и работает с true=(int)1, а false=(int)0... Кривотина какая то... :)
Наверное, будет работать без варнинга только для BOOL, или что там у нас определено, как int...
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390519
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
BagaBagaНо я всё равно не понимаю вашей задачи (да она и не озвучена).

Все просто - создается массив структур:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
struct A 
{
double var_n_const; // значение определяется 1 раз
...
var<double> var_n; // значение меняется при каждом вычислении, значение хранится во внешнем массиве
};

struct В
{
double var_n_const; // значение определяется 1 раз
...
double var_n; // значение меняется при каждом вычислении, значение хранится локально
};



Чтобы обнулить var_n для структур B, нужно в цикле их все перебрать и задать B.var_n=0. В то время для обнуления структур А достаточно с помощью memset обнулить массив, где хранятся значения. Если обнуление делается много миллионов раз, то второй способ быстрее. Аналогично, можно в любой момент сделать "снимок" системы, сохранив массив значений в отдельный буфер и восстановить систему, загрузив массив из буфера. Опять же это быстрее циклического перебора структур.

BagaBagaсоздание простого массива (через new) и его последующая инициализация с memset по сравнению с vector<int>v(elem_number, 0) давало выигрыш менее 0.5%.

А кто сказал, что внешним массивом не может быть вектор? Речь идет не об инициализации массива, а о массовых операциях изменения значений массива в сравнении с изменением значений каждой структуры в цикле.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390537
Anatoly MoskovskyandreybsИзвестно, что обращение к переменной по указателю type* осуществляется в два шага: считывание адреса из указателя, переход по адресу. В то же время ссылка type& содержит прямой адрес на значение переменной в памяти, поэтому обращение по ссылке занимает на один шаг меньше. При выполнении высокопроизводительных операций эта разница ощутима
Это не так.
На уровне машинного кода реализация указателя и ссылки одинаковая, поэтому никакой разницы в скорости у них нет. (Где вы вообще про такое прочли?)

Соответственно, необходимость изменения ссылок надумана. Используйте указатели.
Ссылка int & соответствует int *const __restrict - в этом случае они всегда соптимизируются одинаково. Если указатель без const или __restrict, то у него меньше возможностей к оптимизации, чем у ссылки.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390539
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
int *const __restrict,

А сможете привести пример, когда будет разный код сгенерирован? :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390541
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
int *const __restrict,

Если обычному указателю приписать __restrict, то у него будет больше возможностей к оптимизации?

т.е. в моем случае вместо
T* _ptr;
использовать
T* __restrict _ptr;

Есть еще хинты в оптимизации работы указателей, на которые следует обратить внимание?
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390543
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs,

Вы не до конца поняли. Нужно и const и __restrict.
То есть вы не сможете в указатель присваивать :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390547
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Топикстартеру на заметку:
Вообще, обнуление структур через memset - штука небезопасная, надо знать, какие структуры. POD структуры — все будет ок. Не POD — будет беда.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390552
BagaBaga
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs,
похоже, я не могу понять вас (используемую вами терминологию).

Если у нас есть массив
my_class arr[100]
то у меня в каждом элементе массива хранится одна структура.
Это значит, что я её могу обойти, например, так:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
struct A{
    int a;
};

int main()
{
    A a[100];
    int i =0;
    for(auto& e : a){
        e.a=i++;
    }
    a[12].a=-1;
    for(const auto& e : a)
    {
        cout << e.a << endl;
    }  
   return 0;
}



Я это к тому, что в любом случае ваше var<double> var_n хранится в структуре. Это, конечно, может быть указатель (или ссылка) на что-то "снаружи". И вот тут я начинаю непонимать: речь идёт об "очистке" одной структуры? Тогда вы работаете с одной единицей и "валовые операции" вам не нужны. Вы меняете что-то для "кучи" структур? Но как тогда построить соответствие между местом в массиве (или векторе или что вы там используете) и местом в этом "снаружи"?

То, что вы озвучиваете, мне напоминает ситуацию:
у меня есть два поля, одно из которых меняется редко, а другое - часто. Заведу под них массивы:
double rare[maxsize]
double freq[maxsize]
Но мне же почему-то неудобно показать, что это два массива. Поэтому я сделаю псевдообвязку, в которой одно поле будет указывать (а уж как - через указатели или ссылки - не важно) на rare[i], а во второй - на freq[i]. И заведу массив такого же размера для обвязок (не создавать же ручками кучу переменных). Вот только тогда и можно "показать фокус" и почистить скопом freq, не пробегаясь по массиву обвязок. Но это автоматически означает, что у вас в массиве freq могут храниться только "плоские" POD-типы. Про структуры и классы придётся забыть - по причине особенностей хранения данных (например, вследствие выравнивания структур) как минимум.

А так, без примера очищения "скопом", я не могу понять, о чём вообще речь.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390620
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
MasterZivТопикстартеру на заметку:
Вообще, обнуление структур через memset - штука небезопасная, надо знать, какие структуры. POD структуры — все будет ок. Не POD — будет беда.

Обнуляются не структуры, а только некоторые их члены, значение которых вынесено в отдельный массив данных по указателю. В итоге обнуляется массив данных, а не массив структур.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390624
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
BagaBagaТо, что вы озвучиваете, мне напоминает ситуацию:
у меня есть два поля, одно из которых меняется редко, а другое - часто. Заведу под них массивы:
double rare[maxsize]
double freq[maxsize]
Но мне же почему-то неудобно показать, что это два массива. Поэтому я сделаю псевдообвязку, в которой одно поле будет указывать (а уж как - через указатели или ссылки - не важно) на rare[i], а во второй - на freq[i]. И заведу массив такого же размера для обвязок (не создавать же ручками кучу переменных). Вот только тогда и можно "показать фокус" и почистить скопом freq, не пробегаясь по массиву обвязок.

Вы все верно поняли. Такой "изворот" нужен из-за того, что сами вычисления делаются поэлементо со структурами и их членами (от этого не уйти), а инициализация данных для вычислений осуществляется массово за один раз через внешние массивы с данными (для ускорения каждого цикла вычислений).
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38390628
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly Moskovskyandreybs,

Вы не до конца поняли. Нужно и const и __restrict.
То есть вы не сможете в указатель присваивать :)

Если я правильно понял, что написано здесь и здесь , то __restrict имеет определенный смысл - мы как бы привязываем указатель к определенной области памяти и обещаем его не перезаписывать указателями из других областей. Как раз мой случай, а отсутствие const как раз разрешает менять указатель. Полагаю, нельзя будет ему присваивать NULL - не проблема, можно инициализировать статической переменной типа static T __restrict init_var; (если такая запись проканает). Вобщем, надо будет попробовать и посмотреть на изменение скорости работы...
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38391028
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs,

Да не будет никакого изменения в скорости.
Вы не там оптимизируете.
Оптимизировать надо алгоритм, а не машинный код.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38391113
*const __restrict
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly Moskovskyint *const __restrict,

А сможете привести пример, когда будет разный код сгенерирован? :)
Сходу не приведу, но недавно встречался как раз такой случай, когда заданные в коде константы в compile-time проваливались глубоко внутрь кода через *const __restrict, и сильно оптимизировали код, в том время, как без этих квалификаторов такого не происходило.

andreybs ,
Вообще это C-style подход.
А в C++ нет ничего проще, чем использовать ссылку &, если мы конечно работаем не в google, где разрешены только константные ссылки в параметрах функций :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38391439
BagaBaga
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsОбнуляются не структуры, а только некоторые их члены, значение которых вынесено в отдельный массив данных по указателю. В итоге обнуляется массив данных, а не массив структур.
Вот тут у меня с вами принципиальное расходение в терминологии: ничего в структурах не обнуляется. Там как хранились "редкие данные", так и хранятся. Как хранился адрес (в виде указателя или ссылки - не важно) на "частые" данные, так и хранится. А что происходит с тем куском памяти, ссылка на который хранится в структуре, структуре глубоко всё равно - хоть обнуляйте, хоть генератором случайных чисел заполняйте.

И да, похоже, сначала нужно хорошо оптимизировать алгоритм, а уже потом пытаться выиграть на "массовом заполнении" вместо "индивидуального".
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38391501
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
*const __restrict andreybs ,
Вообще это C-style подход.
А в C++ нет ничего проще, чем использовать ссылку &, если мы конечно работаем не в google, где разрешены только константные ссылки в параметрах функций :)

Мне нужно инициализировать ссылку не в момент создания экземпляра содержащий ее структуры, а после создания всех экземпляров структур.

Кстати, я стараюсь везде использовать константные ссылки в качестве параметров функций... :) Не знаю, как там оптимизатор решит, но так точно не будет создаваться копии параметра в области видимости функции...
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38391503
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
BagaBagaВот тут у меня с вами принципиальное расходение в терминологии: ничего в структурах не обнуляется. Там как хранились "редкие данные", так и хранятся. Как хранился адрес (в виде указателя или ссылки - не важно) на "частые" данные, так и хранится. А что происходит с тем куском памяти, ссылка на который хранится в структуре, структуре глубоко всё равно - хоть обнуляйте, хоть генератором случайных чисел заполняйте.


Разумеется, в структурах физически ничего не обнуляется, ведь они содержат указатели. :)

BagaBagaИ да, похоже, сначала нужно хорошо оптимизировать алгоритм, а уже потом пытаться выиграть на "массовом заполнении" вместо "индивидуального".

Какая основная идея оптимизации многократно повторяющейся последовательности? - Упростить тело цикла.
Вот как раз замена вложенных циклов на "массовые операции" и позволяют ускорить тело цикла, пусть даже в ущерб быстродействию подготовки данных.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38392842
BagaBaga
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если нельзя, но очень хочется, то ...
Код: plaintext
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.
#include <iostream>

using namespace std;

struct S{
    int& a;
    int& b;
    S(int& _a, int& _b):a{_a},b{_b}{};
    S(const S& s):a{s.a},b{s.b}{};
};

int main()
{
    int i =0, j=1, k=2, l=3;
    S s1(i,j); // bind struct references
    S s2(k,l);// another ont
    S s3(s2);//s3 is pointing to k and l
    cout <<&k << endl << &s2.a << endl << &s3.a << endl <<"Hello World" << endl;
    
    // !!!VERY!!! DIRTY TRICK
    //never do it unless you really understand what is going on
    // and have no any - ANY - other way
       ::new (static_cast<void*>(&s3)) S(s1);     
    // now s3 is pointing to i and j - the same as s1

    cout <<&i << endl << &s1.a << endl << &s3.a << endl <<"Hello World" << endl;
    cout << "i was          " << i << endl;
    s3.a = -100;
    cout << "and now i is   " << i << endl;
    i = 111111;
    cout << "and again i is " <<s3.a << endl;
  
   return 0;
}




andreybsКакая основная идея оптимизации многократно повторяющейся последовательности? - Упростить тело цикла.
Вот как раз замена вложенных циклов на "массовые операции" и позволяют ускорить тело цикла, пусть даже в ущерб быстродействию подготовки данных.
Очень просто - путём уменьшения количества операций. Т.е. выноса обнулений за циклы и т.п.

Используемый вами механизм тоже не даётся даром. Вы используете по одной ссылке на каждый элемент массива (в своей структуре-обёртке). Она не даётся даром. На указатель нужна память, в размере (в большинстве реализаций) 32bit (64-разрядные системы для простоты не рассматриваю). Это значит, что для "частых" данных вы практически удваиваете необходимое количество памяти - на сами данные и на указатель на эти данные.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38392889
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
BagaBagaЕсли нельзя, но очень хочется, то ...


Я думал об этом. Отказался от этого подхода из-за того, что в структурах есть еще и массивы указателей на другие связанные структуры. Частое копирование массивов - дело невыгодное для производительности, а передача ссылки на массив по цепочке выглядит не слишком красиво и не прозрачно. Непонятно, кто должен уничтожать массив при уничтожении структуры, значит потребуется добавить деструктор и т.д. структура плавно превращается в класс...

BagaBagaИспользуемый вами механизм тоже не даётся даром. Вы используете по одной ссылке на каждый элемент массива (в своей структуре-обёртке). Она не даётся даром. На указатель нужна память, в размере (в большинстве реализаций) 32bit (64-разрядные системы для простоты не рассматриваю). Это значит, что для "частых" данных вы практически удваиваете необходимое количество памяти - на сами данные и на указатель на эти данные.

Согласен, ничего не дается даром, я жертвую избыточным объемом данных. Но в моем случае это приемлемые потери, ибо одновременно удерживаемых в памяти структур с такими указателями не так много (не более 2-3 тыс), а вот обращение к ним весьма интенсивное. Да и "частых" данных всего 3 поля double, но они ооочень "частые"... :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38392891
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs,

А что за прикладная область?
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38392914
BagaBaga
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybs,
вообще-то этот код
Код: plaintext
1.
::new (static_cast<void*>(&s3)) S(s1);


делает ровно то, что спрашивается в заголовке топика: он изменяет адрес ссылки (в данном случае две ссылки, хранящиеся внутри структуры). Или вам было нужно что-то другое? Но тогда что?
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38392918
BagaBaga
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если нужно пересвязать с другими переменными, то можно и так (используя другой конструктор)
Код: plaintext
1.
::new (static_cast<void*>(&s3)) S(v1,v2); 


в s3 будут храниться ссылки на v1 и v2, вне зависимости от того, но что они ссылались раньше.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38392975
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly Moskovskyandreybs,

А что за прикладная область?

Нейронные сети. Иду на рекорд... :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38392977
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
BagaBagaandreybs,
вообще-то этот код
Код: plaintext
1.
::new (static_cast<void*>(&s3)) S(s1);


делает ровно то, что спрашивается в заголовке топика: он изменяет адрес ссылки (в данном случае две ссылки, хранящиеся внутри структуры). Или вам было нужно что-то другое? Но тогда что?

Вы все верно ответили по теме, спасибо за совет. Просто момент создания темы я ошибочно полагал, что ссылки по определению быстрее указателей... Спасибо форумчанам, помогли разобраться и это помогло мне найти неплохое решение.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38392996
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsAnatoly Moskovskyandreybs,

А что за прикладная область?

Нейронные сети. Иду на рекорд... :)
А, тупиковая ветвь ИИ.
Самое неблагодарное занятие - оптимизировать по крохам алгоритм, чью работоспособность еще предстоит доказать :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38393120
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly MoskovskyА, тупиковая ветвь ИИ.
Самое неблагодарное занятие - оптимизировать по крохам алгоритм, чью работоспособность еще предстоит доказать :)

Довольно субъективное и весьма спорное утверждение. :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38393177
?
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
?
Гость
andreybs, у вас обнуление - это самая частая операция с данными, чтобы оптимизировать скорость ее выполнения за счет замедления других операций?
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38393195
?
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
?
Гость
andreybsВсе просто - создается массив структур:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
struct A 
{
double var_n_const; // значение определяется 1 раз
...
var<double> var_n; // значение меняется при каждом вычислении, значение хранится во внешнем массиве
};

struct В
{
double var_n_const; // значение определяется 1 раз
...
double var_n; // значение меняется при каждом вычислении, значение хранится локально
};



Чтобы обнулить var_n для структур B, нужно в цикле их все перебрать и задать B.var_n=0. В то время для обнуления структур А достаточно с помощью memset обнулить массив, где хранятся значения. Если обнуление делается много миллионов раз, то второй способ быстрее.Да, но только любое обращение (и чтение и запись) к A.var_n медленнее, чем к B.var_n
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38393480
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsAnatoly MoskovskyА, тупиковая ветвь ИИ.

Довольно субъективное и весьма спорное утверждение. :)
Что значит субъективное?
НС придуманы уже полвека как.
Может и ИИ существует уже? Вот то-то же
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38394162
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
?andreybs, у вас обнуление - это самая частая операция с данными, чтобы оптимизировать скорость ее выполнения за счет замедления других операций?

Да, одна из...

?Чтобы обнулить var_n для структур B, нужно в цикле их все перебрать и задать B.var_n=0. В то время для обнуления структур А достаточно с помощью memset обнулить массив, где хранятся значения. Если обнуление делается много миллионов раз, то второй способ быстрее.Да, но только любое обращение (и чтение и запись) к A.var_n медленнее, чем к B.var_n[/quot]

Экспериментальным путем выявлено, что замена переменных на указатели замедляет работу в среднем на 300тиков на миллион итераций вычисления 3,5%. Переход на указатели дал не менее 17% прироста производительности при обучении сети. Поскольку обучение - самая тяжелая операция, то переход на указатели оправдан.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38394165
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly Moskovskyandreybsпропущено...

Довольно субъективное и весьма спорное утверждение. :)
Что значит субъективное?
НС придуманы уже полвека как.
Может и ИИ существует уже? Вот то-то же

Ну, генетику придумали тоже не вчера, но человека пока никто не клонировал. Тем не менее, это не мешает использовать генетику в промышленных отраслях.

И кто вам сказал, что "ИИ" не существует? Приведите определение "ИИ"?
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38394203
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsИ кто вам сказал, что "ИИ" не существует? Приведите определение "ИИ"?
Давайте не будем :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38394269
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly MoskovskyandreybsИ кто вам сказал, что "ИИ" не существует? Приведите определение "ИИ"?
Давайте не будем :)

Вот, народ насмотрится фильмы про всяких терминаторов, потом думает, что ИИ - это неубиваемый металлический чувак с пулеметом... :)

На самом деле прототипы ИИ уже давно существуют:
- Deep Blue
-Системы распознавания речи , а так же системы распознавания образов, системы ведения цели (наведения на цель).
- MYCIN
- IBM Watson
и т.д...

Другое дело, что элементная база пока не доросла. Но пройдет несколько десятков лет и найдут новую базу (может смогут строить квантовые компьютеры ) и тогда вопрос с параллельными вычислениями будет решен кардинально, а построение сложного ИИ станет вполне реальным.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38394272
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Кстати, оказывается Google уже анонсировал в этом году проект, о котором я написал выше . Не исключаю, что 90% (или больше) сочтут это полным бредом и будут тихо посмеиваться в стороне. Но лично я считаю это вполне реальным проектом, который может дать серьезный толчок в сфере разработки суперкомпьютеров и ИИ.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38394567
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsНо пройдет несколько десятков лет
Что и требовалось доказать. ИИ на НС - тупиковая ветвь.
Как и летательные аппараты с машущими крыльями.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38394641
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly Moskovsky,

Вы знаете более перспективные направления ИИ, раз нейросети считаете тупиковыми? :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38394649
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Лучшие ИИ строятся на ГСЧ. Стохастические алгоритмы рулят.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38394932
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreybsВы знаете более перспективные направления ИИ, раз нейросети считаете тупиковыми? :)
На данный момент перспективных нет. И это не смешно.
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38395098
andreybs
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly Moskovsky,

Полагаю, это вопрос терминологии - что называть "настоящим ИИ" и какие направления считать "перспективными". Вот, товарисч с постом выше про стохастические методы (немного не в тему) напомнил про стохастические модели, которые позволяют реализовать логику квантового процессора, на базе которого за счет высокой степени параллелилизма вычислений можно реализовывать самые сложные нейросети, а следовательно и ИИ. Куда уж еще перспективнее? На тех же принципах строится обработка информации в мозгу человека. Хочется верить, что у нас в голове реализованы самые перспективные технологии из известных в наше время... :)
...
Рейтинг: 0 / 0
изменить адрес ссылки
    #38395116
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Модератор: Топик закрыт, тема себя исчерпала, далее идёт только оффтопик.
...
Рейтинг: 0 / 0
63 сообщений из 63, показаны все 3 страниц
Форумы / C++ [игнор отключен] [закрыт для гостей] / изменить адрес ссылки
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]