Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Зачем нужна Boost.Coroutine 
V
    Опции темы
bsa
Дата 5.1.2013, 13:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



В следующей версии boost будет включена библиотека coroutine. Прочитав описания стало понятно, как ее использовать, и как она работает. Но для меня пока остается загадкой ответ на вопрос: А ЗАЧЕМ? Единственное, что я могу предположить - деление больших неделимых операций на несколько маленьких. И это все? Может еще зачем-то она нужна и мне это позарез необходимо, но я не понимаю?
Одно точно я вижу, структура программы, использующей ее, значительно усложнится. И без внятной документации понять принцип работы программы будет ой как затруднительно. Подобное происходит, например, с asio. Но там хоть понятно - поставил задачу, до нее дошла очередь, она выполнилась. А тут? Работаешь, сохранил контекст, прыгнул (восстановил другой контекст) в середину другой функции, поработал, прыгнул в середину третьей...  smile 
PM   Вверх
NoviceF
Дата 5.1.2013, 15:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 313
Регистрация: 13.3.2012
Где: Ростов-на-Дону

Репутация: нет
Всего: 2



фанаты goto давно ждали возможности для реванша  smile 
PM MAIL   Вверх
bsa
Дата 6.1.2013, 13:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(NoviceF @  5.1.2013,  16:49 Найти цитируемый пост)
фанаты goto давно ждали возможности для реванша
 smile 
Реванш слишком жестокий...
Кстати, хочется услышать мнение boostcoder на этот счет - он сильно радовался этой либе.
PM   Вверх
maxim1000
Дата 6.1.2013, 20:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 17
Всего: 110



если не злоупотреблять, штука довольно полезная
как минимум удобно при обработке последовательностей, при конвертации всяких данных, в некоторых алгоритмах (особенно тех, которые являются комбинацией нескольких сложных кусков)

представим себе, что у нас есть последовательность 123456789101112131415... (цифры всех чисел записаны друг за другом), нужно посчитать сумму всех элементов на чётных местах до 1000-го.

вывести символы последовательности одним циклом по номеру числа - проще простого, посчитать искомую сумму другим циклом по номеру символа - ещё проще

проблемы:
1. предварительно считать может будть накладно по памяти
2. далеко не для всех алгоритмов можно заранее сказать, сколько понадобится элементов без запуска алгоритма

самый простой выход - объединить два куска кода, т.е. вместо закидывания во временное хранилище мы просто отбрасываем нечётные, а чётные складываем

проблемы:
1. для одного алгоритма естественен цикл по числам, для второго - по символам, что бы мы ни выбрали, код одного из них станет сложнее
2. переменые, отвечающие за состояния алгоритмов (аккумулятор, номер числа) доступны обоим алгоритмам
3. алгоритм генерации последовательности и вычисления искомого значения, по-хорошему, не должны знать ничего друг о друге, тогда легче станет менять последовательности и вычисления независимо
4. плохо масштабируется (ведь может быть целая цепочка преобразований последовательностей)

следующий выход - сконвертировать один из алгоритмов во что-то вроде функтора и дёргать его из другого, пример (не для вышеописанной задачи) - std::istream_iterator и какой-нибудь алгоритм из namespace std, можно даже и то, и то, оставив главный цикл одиноким в своей функции (std::istream_iterator, std::find_if, функтор для отсева)

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

а в случае с coroutines что построение последовательности, что вычисление искомой суммы представляются обычными циклами, циклы эти находятся в разных функциях, друг о друге почти ничего не знают, переменные состояния скрыты, всё хорошо

P.S.
с Boost.Coroutines я не работал, подробностей не знаю, описывал общее понятие coroutines


--------------------
qqq
PM WWW   Вверх
EvilsInterrupt
Дата 6.1.2013, 23:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

Репутация: 2
Всего: 9



maxim1000
Если не сложно, то можно еще один пример где пригодится эта новая фича? Я привык понимать на нескольких.
PM MAIL WWW ICQ Jabber   Вверх
boostcoder
Дата 7.1.2013, 14:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



Цитата(bsa @  6.1.2013,  13:40 Найти цитируемый пост)
хочется услышать мнение boostcoder

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

зы
на самом деле, в связи с беспределом администрации сего ресурса(от чем я неоднократно писал тут), мне множество раз предлагали сменить ресурс, многие из тех кто еще недавно пользовался этим форумом и перестали.
PM WWW   Вверх
NoviceF
Дата 7.1.2013, 14:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 313
Регистрация: 13.3.2012
Где: Ростов-на-Дону

Репутация: нет
Всего: 2



 smile а чего было то, чего было?!?!
PM MAIL   Вверх
maxim1000
Дата 7.1.2013, 14:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 17
Всего: 110



Цитата(EvilsInterrupt @  6.1.2013,  23:54 Найти цитируемый пост)
Если не сложно, то можно еще один пример где пригодится эта новая фича?

хм...
ну например, вот:

есть у нас какая-то древовидная структура из каких-то объектов
у каждого объекта есть цвет
нам нужно пробежаться по объектам и последовательность цветов запаковать как-нибудь (то ли RLE, то ли Zip)

самая простая пробежка по дереву - рекурсивная
для запаковки, как правило удобно просто бежать циклом по входному потоку

coroutines позволяют запихнуть пробежку по дереву в отдельную функцию и выдавать по одному значению, а запаковку - в другую, с простым циклом по значениям


--------------------
qqq
PM WWW   Вверх
Pfailed
Дата 7.1.2013, 14:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 933
Регистрация: 19.7.2009

Репутация: нет
Всего: 39



Сопрограммы можно эффективно использовать в сетевом программировании. Предположим в одной функции вы произвели неблокирующий connect, следующая операция в этой функции -- запись в приконнекченный сокет. Естественно пока сокет не законнектится записывать бессмысленно, соответственно имеет смысл на это время переключиться в другую функцию и выполнить другое полезное действие. Таким образом получаем эффективное приложение и все в рамках одного процесса и потока.


--------------------
PM MAIL   Вверх
EvilsInterrupt
Дата 7.1.2013, 15:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

Репутация: 2
Всего: 9



Цитата

coroutines позволяют запихнуть пробежку по дереву в отдельную функцию и выдавать по одному значению, а запаковку - в другую, с простым циклом по значениям 

Очень похоже на yeld в генераторах Python и др. современных скриптовых языках.
PM MAIL WWW ICQ Jabber   Вверх
baldina
Дата 7.1.2013, 23:07 (ссылка) |    (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(bsa @  5.1.2013,  13:11 Найти цитируемый пост)
Одно точно я вижу, структура программы, использующей ее, значительно усложнится.

Цитата(bsa @  5.1.2013,  13:11 Найти цитируемый пост)
А тут? Работаешь, сохранил контекст, прыгнул (восстановил другой контекст) в середину другой функции, поработал, прыгнул в середину третьей...

с точностью до наоборот. вы слишком вникаете в технику исполнения, а следует думать концептуально.
технически: сопрограмма имеет состояние, в которое возвращается при каждом последующем вызове. вызов завершается "оператором" возврата yield

Цитата(EvilsInterrupt @  7.1.2013,  15:51 Найти цитируемый пост)
Очень похоже на yeld в генераторах Python

да.
можно сказать, что сопрограммы реализуют yield, а остальное вытекает из этого факта.
точно также можно сказать, что сопрограммы имеют состояние и множество точек входа, из которых конкретная определяется состоянием. из этого вытекает возможность реализации yield.

сопрограммы - всего лишь инструмент, без него можно жить, но есть ситуации, когда использование сопрограмм дает более короткие и ясные программы.
обычно выделяют три ситуации:
1. конечные автоматы. здесь польза прямо вытекает из определения: сопрограмма меняет состояния автоматически, без привлечения специальных механизмов. пример:
для реализации автомата, который безусловно переходит в состояния 1->2->3 достаточно написать функцию-сопрограмму
Код

int getState () {
  for (int i=1; i <= 3; ++i)
    yield (i); // здесь сопрограмма возвращает значение (состояние автомата); при следующем вызове getState() продолжится с точки возврата, т.е. при текущем значении i
}

в boost::coroutine все не так лаконично
Код

int getState (coro::generator<int>::self& self) {
  int i=1;
  while (i <= 3) {
    self.yield (i++);
 }
 return i;
}

но идея ясна; это значительно экономичнее и прозрачнее конструкции
Код

int getState () {
  static int state = 0;
  switch (state) {
    case 0: return 1;
    case 1: return 2;
    case 2: return 3;
    default: throw (invalid_state());
  }
}

а если надо иметь несколько контекстов, вместо static int state потребуется целый огород.

2. генераторы. функции, генерирующие последовательность. возвращаемое значение можно интерпретировать как итератор.
Код

int fac () {
  int i=0;
  int f=1;
  while (true) {
    yield (f*=i++);
  }
}

int main () {
  for (int i=0; i < 10; ++i)
    cout << "Factorial " << i << "=" << fac() << endl;
}

в boost::coroutine и с итераторами:
Код

int fac_generator(generator_type::self& self)
{
  int i=0;
  int p=1;
  while (true) {
    p *= i++; 
    yield (p);
  }
  return p;
}

int main () {
  generator_type generator  (boost::bind  (fac_generator, _1));
  for (int i=0; i < 10; ++i) 
    cout<<"Factorial " << i << "=" << *generator++<<endl;
}

немного похоже на fsm, правда? действително, fsm - обобщение генераторов
в С++ без сопрограмм в простейшем случае нужно поддерживать контейнер для исходящих значений либо лишние параметры, в более сложном - слишкоммногабукф


3. модель взаимодействия акторов (actors). похоже на взаимодействие объектов в ООП (посылкой сообщений\вызовом методов), но отличается независимостью взаимодействующих единиц (вплоть до их параллельного исполнения). это модель типа источник/приемник (producer/consumer), ближайшая аналогия - межсетевое взаимодействие (по протоколу).
т.к. акторы (actors) являются сопрограммами, каждая может "думать" в удобных ей единицах: пакетах, запросах и т.п..
это как раз тот пример, который привел maxim1000. более классический пример - взаимодействие лексического и синтаксического анализатора: каждый является функцией, они исполняются параллельно, но у каждого свои "вехи": лексическому анализатору удобно мыслить в терминах лексем, синтаксическому - в терминах конструкций.



Это сообщение отредактировал(а) baldina - 8.1.2013, 01:27
PM MAIL   Вверх
volatile
Дата 8.1.2013, 00:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



baldina, все замечательно, только ваши примеры делают не то что написано, если я не ошибаюсь конечно.

Цитата(baldina @  7.1.2013,  23:07 Найти цитируемый пост)
int fac () {
  int i=1;
  while (true) {
    yield (i*i++);
  }
}

Это же не генератор факториалов, а генератор квадратов, или что-то типа того...

Цитата(baldina @  7.1.2013,  23:07 Найти цитируемый пост)
int fac_generator(generator_type::self& self)
{
  int i=1;
  while (true) {
    i *= i+1; 
    yield (i);
  }
  return i;
}

И это тоже не генератор факториалов.

ген. факториалов будет как-то так:
Код

int fac () {
  int i = 0;
  int prod = 1;
  while (true) {
    yield (prod *= ++ i);
  }
}

Извините что вмешиваюсь, но примеры сбивают с толку просто.


Это сообщение отредактировал(а) volatile - 8.1.2013, 00:10
PM MAIL   Вверх
bsa
Дата 8.1.2013, 00:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(baldina @  8.1.2013,  00:07 Найти цитируемый пост)
yield (i*i++);
Исправь, это же типичное UB.
В целом, я пока не проникся... Что-то гложат меня сомнения об эффективности смены контекста (значений всех регистров) по сравнению с вызовом функции (которая может быть встроена, если компилятор это посчитает разумным).
Работа с потоками уже, вроде, решена в ASIO. По сути, там тоже самое, только через вызовы функций. Хотя, может в случае парсеров все несколько иначе - я ими не особо занимался.
Проблема генераторов решается путем выноса их контекста за пределы функций (типичный класс), причем, это будет эффективнее сопрограмм - нет восстановлений регистров процессора.
Я неправ?
PM   Вверх
baldina
Дата 8.1.2013, 00:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(volatile @  8.1.2013,  00:07 Найти цитируемый пост)
Это же не генератор факториалов

да. был пьян, поторопился)))
ну, суть ясна, я думаю

Добавлено через 13 минут и 51 секунду
bsa, все это так.

Цитата(bsa @  8.1.2013,  00:09 Найти цитируемый пост)
Что-то гложат меня сомнения об эффективности смены контекста (значений всех регистров) по сравнению с вызовом функции

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

вопрос не в том, как реализовать фичу, а как реализовать удобным образом.

как я понимаю, yield это часть концепции функционального подхода (и хотя в явном виде далеко не во всех функциональных языках присутствует, три описанные ситуации так или иначе в функциональных языках обычно реализуемы).

сопрограммы естественно реализуемы на ассемблере (как это описано у Кнута), а в языках, где есть лишь подпрограммы, реализация требует известных ухищрений.

можно использовать или не использовать ООП, обобщенное программирование. можно обойтись без мультиметодов, сравнения с образцом, делегатов и лямбда-функций и т.д. Однако имея эти средства, можно что-то делать яснее и короче. Только и всего.

с точки зрения реализации самый простой пример преимущества сопрограмм - возможность отказа от switch в тех ситуациях, когда его нельзя заменить на полиморфизм.
PM MAIL   Вверх
baldina
Дата 8.1.2013, 01:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



м-да, с примерами не очень. исправил  smile

Добавлено через 7 минут и 32 секунды
Цитата(bsa @  8.1.2013,  00:09 Найти цитируемый пост)
это будет эффективнее сопрограмм - нет восстановлений регистров процессора

bsa, мне даже неловко вам говорить о преждевременной оптимизации...
но вообще говоря, любое высокоуровневое решение может повлечь дополнительные накладные расходы. при этом (часто незначительный, по сути) проигрыш в производительности компенсируется выигрышем в затратах на разработку и сопровождение.
в примерах Кнута (на MIXе) накладных расходов нет вообще: сопрограмма отличается от подпрограммы лишь тем, что адрес возврата, помещаемый в стек, всегда разный.

PM MAIL   Вверх
bsa
Дата 8.1.2013, 09:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(baldina @  8.1.2013,  02:04 Найти цитируемый пост)
компенсируется выигрышем в затратах на разработку и сопровождение.

честно говоря, пока я не прочитал еще раз документацию, я не мог понять, как работает генератор из твоего примера. Не уверен, что эта фича упростит понимание программы. Чтобы ее понимать, нужно знать о ней.
PM   Вверх
baldina
Дата 8.1.2013, 15:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(bsa @  8.1.2013,  09:53 Найти цитируемый пост)
Чтобы ее понимать, нужно знать о ней.

 smile 

что такое массив и список, чем они похожи и отличаются, где они хороши и плохи и т.д. должен знать каждый. так?
что такое подпрограммы - тем более. про сопрограммы - из того же разряда.
если бы вопрос крутился не вокруг библиотеки С++, а вокруг языка, непосредственно поддерживающего сопрограммы, вопроса скорее всего просто не возникло бы.

bsa, пока не прочитать про ООП, программисту С непонятно что такое класс, как оно работает и т.п. 
тут то же самое.
я отнюдь не хвалю boost::coroutine и не агитирую за испольование сопрограмм. да и примеры, согласен, лучше специально подготовленные, а не на коленке. но тогда уж надо открывать учебники и читать про концепции, преимущества и пр. 
в книгах это всегда полнее, чем любой ответ на форуме.



PM MAIL   Вверх
bsa
Дата 8.1.2013, 17:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



baldina, думаю ты согласишься с утверждением, что хороший язык программирования отличается от плохого помимо всего прочего ясностью кода. Действия break, return, continue, do, while, if, else и goto вполне очевидны. А вот yield... Ну ладно.
Я понял, что существуют задачи, где использование сопрограмм имеет смысл. Другое дело, что я таких не припомню.
PM   Вверх
volatile
Дата 8.1.2013, 18:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Цитата(baldina @  8.1.2013,  01:04 Найти цитируемый пост)
м-да, с примерами не очень. исправил  

baldina, Еще раз извиняюсь за занудство, исправьте уж нормально, а то теперь у вас генератор нулей получился.

bsa, ну это как рекурсия. любую задачу можно решить без нее, согласитесь.
Но встречаются задачи (изредка) которые с рекурсией получаются гораздо короче и понятней.
С этим точно также.
PM MAIL   Вверх
bsa
Дата 8.1.2013, 19:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



volatile, я не спорю.
PM   Вверх
chaos
Дата 7.3.2013, 13:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Серийный программист
****


Профиль
Группа: Завсегдатай
Сообщений: 2979
Регистрация: 7.7.2004
Где: Екатеринбург

Репутация: 6
Всего: 44



Чото нифига пока понять не могу.

Вопрос по первому же рисунку из доки
user posted image
Что нужно написать в main что бы получить такой же оутпут?
PM WWW   Вверх
bsa
Дата 7.3.2013, 18:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Там речь идет о том, как такое сделать.
PM   Вверх
EvilsInterrupt
Дата 31.3.2013, 12:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

Репутация: 2
Всего: 9



Может кому-то пригодится. Для чего и зачем применять сопрограммы(coroutine). На мой взгляд достаточно лаконично и понятно описано в справочнике по Python Девид Бизли 4. изд. 2010 г.

Пояснения : стр.136 - Замыкания, декораторы, генераторы, сопрограммы
Примеры использования: стр.146(генераторами), стр.42, стр.147 (сопрограммы)

PM MAIL WWW ICQ Jabber   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1678 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.