Oberon space
General Category => Общий раздел => Тема начата: ilovb от Июнь 07, 2012, 07:53:24 am
-
...
Для каждого СтрокаДетальная из СтрокаОткудаКуда.Строки Цикл
Если СтрокаДетальная.Номенклатура <> ТекНоменклатура ИЛИ СтрокаДетальная.Счет <> ТекСчет ИЛИ СтрокаДетальная.КБК <> ТекКБК Тогда
Продолжить;
КонецЕсли;
...
WTF?
Этот перл мне попался в типовой конфигурации.
"СтрокаОткудаКуда.Строки" содержит 6500000 строк. Все это находится в процедуре, которая вызывается несколько тысяч раз.
Проверку проходят только 300000.
Соответственно 20% времени выполнения тратится на этот код. (а время выполнения 30 мин)
Зачем такое писать, если в 1С есть индексированные коллекции с быстрым поиском??? :o
Жесть.
-
Я вообще не понял смысла этого кода. Он же вроде ничего не делает. Усеченный аналог:
foreach (i : is) {
if (condition(i)) continue;
}
Или там после конца если еще какой-то код в теле цикла есть?
PS. continue иногда бывает удобен, но очень-очень редко. Примерно один раз в год. С другой стороны, я вполне верю что можно перенастроить свой мозг так, что continue будет в большенстве циклов и это будет читабельно (и я это даже, эксперимента ради, проделывал). Но я все же предпочитаю работать с кодом у авторов которого мозг повернут примерно так же как у меня :-)
-
Напоминает классику "лучше день потерять, потом за пять минут долететь"...
Индексировать 6,5 млн строк, вернее составных объектов, в 1С, видимо, посчитали излишним.
Причины могут быть разными: от банального "к следующему выпуску ИТС поправим (надо же за что-то деньги драть)", до "сортировать 6,5 млн объектов, где каждый реквизит сам себе объект...". Сколько времени займет построение индексированной коллекции из 6,5 млн элементов, может оно не стоит того на таких данных? Откуда появилось 6,5 млн. объектов?
-
Или там после конца если еще какой-то код в теле цикла есть?
Да. Там код в теле цикла.
...
Для каждого СтрокаДетальная из СтрокаОткудаКуда.Строки Цикл
Если СтрокаДетальная.Номенклатура <> ТекНоменклатура ИЛИ СтрокаДетальная.Счет <> ТекСчет ИЛИ СтрокаДетальная.КБК <> ТекКБК Тогда
Продолжить;
КонецЕсли;
... // тут код!
КонецЦикла;
...
-
Напоминает классику "лучше день потерять, потом за пять минут долететь"...
Индексировать 6,5 млн строк, вернее составных объектов, в 1С, видимо, посчитали излишним.
Причины могут быть разными: от банального "к следующему выпуску ИТС поправим (надо же за что-то деньги драть)", до "сортировать 6,5 млн объектов, где каждый реквизит сам себе объект...". Сколько времени займет построение индексированной коллекции из 6,5 млн элементов, может оно не стоит того на таких данных? Откуда появилось 6,5 млн. объектов?
Ну займет индексирование по трем полям секунды 3, а поиск будет за миллисекунды работать.
-
Зачем такое писать, если в 1С есть индексированные коллекции с быстрым поиском??? :o
Жесть.
Вопрос риторический и ответ - банальный, в следствие низкой квалификации и/или плохого понимания задачи.
-
Ну займет индексирование по трем полям секунды 3, а поиск будет за миллисекунды работать.
Ну, может 1С с тех пор сильно вперед продвинулась, чтобы упорядочить 6,5 млн объектов за 3 секунды, или у меня комп не шибко быстрый был, я не знаю, но на примерно 2,5 млн объектов по 2-м полям оно ~15 минут работало и память жрало нещадно...
-
Чего-то я уже сам засомневался. Может нуль лишний привиделся...
Щас запущу расчет и проверю чего там было.
-
Да заработался....
6,5м и 300к - это была статистика из профайлера. Столько раз отработал этот код.
А в коллекции всего около 1000 строк.
Но один фиг суть не меняется. Тем более 1С-ный код работает на моей машине в 500 раз медленнее нативного. А поиск в индексированных коллекциях нативный, и индексы составные можно строить, и строятся они очень быстро.
-
Ну может ребята захотели форыч использовать, а как его с индексированной коллекцией юзать? Не помню, у них форыч с первого элемента стартует или с текущего?
-
Все коллекции в 1с обходятся форычем. Я кстати замерил время построения составного индекса по трем полям в коллекции из 500000 элементов - 0,7 сек
-
Кстати, подслушивая разговоры наших 1с-ников (один обучал писать циклы другого) с ужасом узнал, что в 1С в цикле Для (For) нет возможности указать шаг приращения счётчика. Правда что ли вам приходится делать так:
Для I = 1 До 100 Цикл
<чо-та тут делаем>
I = I + 3; // шаг цикла = 4 ??? или 3 ???
КонецЦикла
-
Ага :)
-
Но только не в FOR конечно а в WHILE
Делать так в FOR имхо говнокод
-
Накидал маленький тест:
Процедура КнопкаВыполнитьНажатие(Кнопка)
Таблица = Новый ТаблицаЗначений;
Колонки = Таблица.Колонки;
Колонки.Добавить("Раз");
Колонки.Добавить("Два");
Колонки.Добавить("Три");
Колонки.Добавить("Четыре");
Индекс = 0;
Число = 0;
Для Индекс = 0 По 500000 Цикл
НоваяСтрока = Таблица.Добавить();
Если Число > 100 Тогда Число = 0 КонецЕсли;
НоваяСтрока.Раз = Число; Число = Число + 1;
НоваяСтрока.Два = Число; Число = Число + 1;
НоваяСтрока.Три = Число; Число = Число + 1;
НоваяСтрока.Четыре = Индекс;
КонецЦикла;
Таблица2 = Таблица.Скопировать();
Таблица.Индексы.Добавить("Раз, Два, Три");
Число = 0;
Для Индекс = 0 По 1000 Цикл
Если Число > 100 Тогда Число = 0 КонецЕсли;
Раз = Число; Число = Число + 1;
Два = Число; Число = Число + 1;
Три = Число; Число = Число + 1;
ИскомыеСтроки = Таблица.НайтиСтроки(Новый Структура("Раз, Два, Три", Раз, Два, Три));
ИскомыеСтроки = Таблица2.НайтиСтроки(Новый Структура("Раз, Два, Три", Раз, Два, Три));
Обработать1(Таблица, Новый Структура("Раз, Два, Три", Раз, Два, Три));
Обработать2(Таблица, Раз, Два, Три);
КонецЦикла;
КонецПроцедуры
Процедура Обработать1(Таблица, Отбор) // мой способ
ИскомыеСтроки = Таблица.НайтиСтроки(Отбор);
Для Каждого ТекСтрока Из ИскомыеСтроки Цикл
Рез = ТекСтрока.Четыре / 2; // какой-то код
КонецЦикла;
КонецПроцедуры
Процедура Обработать2(Таблица, Раз, Два, Три) // способ с "Продолжить"
Для Каждого ТекСтрока Из Таблица Цикл
Если ТекСтрока.Раз <> Раз ИЛИ ТекСтрока.Два <> Два ИЛИ ТекСтрока.Три <> Три Тогда
Продолжить;
КонецЕсли;
Рез = ТекСтрока.Четыре / 2; // какой-то код
КонецЦикла;
КонецПроцедуры
Статистика профайлера:
http://smotr.im/5idz (http://smotr.im/5idz)
-
Для 3 000 000 строк индекс строится 11 сек.
Ну не угадал я немного с 3-мя секундами ;D
-
Все коллекции в 1с обходятся форычем. Я кстати замерил время построения составного индекса по трем полям в коллекции из 500000 элементов - 0,7 сек
Это понятно, что обходятся. Но я имел ввиду несколько иное. Форыч сейчас пихают куда попало, это как чума какая-то. Вот и парни из 1С тоже поддались поветрию. А если обход форычем начинается не с текущего элемента, на которым можно прыгнуть перед входом в цикл, найдя первый элемент, соответствующий необходимому условию, а с самого первого, то особого смысла упорядочивать коллекцию нет. Для работы с индексированной коллекцией нужно использовать другие подходы, для чего нужно включать мозг.
Что касается скорости построения индекса, то нужно проверять не на целочисленных полях, а на более сложных данных, лучше именно на том наборе данных, который присутствует в конфигурации.
-
Во первых я не слыхал чтобы форыч где-либо стартовал не с первого элемента. Во вторых в общем то и нету разницы с какого элемента он стартует, т.к. форыч обходит КАЖДЫЙ ЭЛЕМЕНТ, т.е. все элементы в данной коллекции.
Для работы с индексированной коллекцией нужно использовать другие подходы, для чего нужно включать мозг.
В смысле?
Что касается скорости построения индекса, то нужно проверять не на целочисленных полях, а на более сложных данных, лучше именно на том наборе данных, который присутствует в конфигурации.
А чем сложнее объект индексировать? И тем более с чего вы взяли, что число в 1С не объект?
-
Вот например нормальное число в 1С:
БольшоеЧисло = 131351354354354853131564346165156434345343543543543543541651531531513513513513513515151315315152132123111313513543543548531315643461651564343453435435435435435416515315315135135135135135151513153151521321231113135135435435485313156434616515643434534354354354354354165153153151351351351351351515131531515213212311131351354354354853131564346165156434345343543543543543541651531531513513513513513515151315315152132123111313513543543548531315643461651564343453435435435435435416515315315135135135135135151513153151521321231113135135435435485313156434616515643434534354354354354354165153153151351351351351351515131531515213212311131351354354354853131564346165156434345343543543543543541651531531513513513513513515151315315152132123111313513543543548531315643461651564343453435435435435435416515315315135135135135135151513153151521321231113135135435435485313156434616515643434534354354354354354165153153151351351351351351515131531515213212311131351354354354853131564346165156434345343543543543543541651531531513513513513513515151315315152132123111313513543543548531315643461651564343453435435435435435416515315315135135135135135151513153151521321231113135135435435485313156434616515643434534354354354354354165153153151351351351351351515131531515213212311131351354354354853131564346165156434345343543543543543541651531531513513513513513515151315315152132123111313513543543548531315643461651564343453435435435435435416515315315135135135135135151513153151521321231113135135435435485313156434616515643434534354354354354354165153153151351351351351351515131531515213212311131351354354354853131564346165156434345343543543543543541651531531513513513513513515151315315152132123111313513543543548531315643461651564343453435435435435435416515315315135135135135135151513153151521321231113135135435435485313156434616515643434534354354354354354165153153151351351351351351515131531515213212311131351354354354853131564346165156434345343543543543543541651531531513513513513513515151315315152132123111313513543543548531315643461651564343453435435435435435416515315315135135135135135151513153151521321231113135135435435485313156434616515643434534354354354354354165153153151351351351351351515131531515213212311;
-
Из встроенной справки:
Для каждого (For each)
Синтаксис:
Для каждого <Имя переменной 1> Из <Имя переменной 2> Цикл
// Операторы
[Прервать;]
// Операторы
[Продолжить;]
// Операторы
КонецЦикла;
Англоязычный синтаксис:
For each <Имя переменной 1> In <Имя переменной 2> Do
// Операторы
[Break;]
// Операторы
[Continue;]
// Операторы
EndDo;
Параметры:
<Имя переменной 1>
Переменная, которой при каждом повторении цикла присваивается значение очередного элемента коллекции.
Из
Синтаксическая связка для параметра <Имя переменной 2>.
<Имя переменной 2>
Переменная или выражение, предоставляющее коллекцию. Элементы этой
коллекции будут присваиваться параметру <Имя переменной 1>.
Цикл
Операторы, следующие за ключевым словом Цикл выполняются, пока значение переменной <Имя переменной> меньше значения <Выражение 2>.
// Операторы
Исполняемый оператор или последовательность таких операторов.
Прервать
Позволяет прервать выполнение цикла в любой точке. После выполнение этого оператора управление передается оператору, следующему за ключевым словом КонецЦикла.
Продолжить
Немедленно передает управление в начало цикла, где производится вычисление и проверка условий выполнения цикла. Операторы, следующие в теле цикла за ним, на данной итерации обхода не выполняются.
КонецЦикла
Ключевое слово, которое завершает структуру оператора цикла.
Описание:
Оператор цикла Для каждого предназначен для циклического обхода коллекций значений. При каждой итерации цикла возвращается новый элемент коллекции. Обход осуществляется до тех пор, пока не будут перебраны все элементы коллекции.
-
Во первых я не слыхал чтобы форыч где-либо стартовал не с первого элемента. Во вторых в общем то и нету разницы с какого элемента он стартует, т.к. форыч обходит КАЖДЫЙ ЭЛЕМЕНТ, т.е. все элементы в данной коллекции.
Итераторы и Форычи бывают разные, в том числе и те, которые позволяют обходить коллекцию и с конца и с произвольного (текущего) элемента. Ну и из Форыча можно выскочить на любой итерации (при наличии механизмов выхода из цикла).
Для работы с индексированной коллекцией нужно использовать другие подходы, для чего нужно включать мозг.
В смысле?
Ну форыч проще, поэтому его часто и используют, я же говорю - поветрие.
А с индексированной коллекцией форыч стартующий всегда с первого элемента не эффективен в плане производительности - всё равно всю коллекцию обойдём, так зачем её индексировать? Я именно об этом и говорил - такой говнокод (приведённый Вами в первом сообщении) получился по причине использования форыча.
А чем сложнее объект индексировать? И тем более с чего вы взяли, что число в 1С не объект?
Ни с чего не взял, как раз наоборот, где-то в другой ветке я говорил что в 1С всё объект.
Но объект объекту рознь. Это ж по-сути похоже на вариант, и число там всё-таки хранится как число, а не как строка, к примеру, а индексировать по числу всяко быстрее индексирования строковых данных.
Но может, как я уже предполагал, в 1С всё-таки, допилили индексирование и оно стало рективным, что очень приятно, но когда, несколько лет назад, я обращался в саппорт 1С на предмет тормозов при построении индекса, и даже отправлял им тестовую конфу с данными на экспертизу, мне ответили, что это нормально, особой производительности на данном наборе данных и такого объема ждать не стоит. И предложили использовать временные таблицы, запросы и еще что-то, уже не помню. Поэтому от индексированной коллекции тогда пришлось отказаться ((
-
Скачал сейчас последний релиз 8.2 погоняю, может стоит вернуться на 1С, а то тогда пришлось перейти на Delphi и писать всё ручками.
-
Раз уж речь снова зашла об 1С, подскажи пожалуйста нормальную документацию к языку. Встроенный справочник на первый взгляд очень неудобен.
-
Berserker, а чем комплект книг из поставки не устраивает? Там всё очень подробно на 4 или 5 томов.
Есть еще пара-тройка хороших книг других авторов, но названия не скажу, они у меня дома, а дома я окажусь только после 21-го. В электронном виде не видел, видимо 1С прибивает такие сайты.
-
Мне более в учебных целях, так что 5 томов - это определённо не торт. Но всё равно спасибо.
-
Все книги Радченко. Для начинающих 'Практическое пособие разработчика'. Для продолжающих 'Профессиональная разработка'. Это самые лучшие источники. Других авторов читать не советую. Если автор не из 1с то в книгу лучше вообще не заглядывать, т.к. придется потом переучиваться
-
А с индексированной коллекцией форыч стартующий всегда с первого элемента не эффективен в плане производительности - всё равно всю коллекцию обойдём, так зачем её индексировать?
Зачем всю обходить? ???
Вот жеш:
Процедура Обработать(Таблица, Отбор) // вызывается много много раз!
ИскомыеСтроки = Таблица.НайтиСтроки(Отбор);
Для Каждого ТекСтрока Из ИскомыеСтроки Цикл
Рез = ТекСтрока.Четыре / 2; // какой-то код
КонецЦикла;
КонецПроцедуры
ИскомыеСтроки - это массив строк из коллекции, отобранный быстрым поиском по индексу.
Обходим только его.
Вроде все элементарно. И очевидно, что это будет быстрее.
-
ilovb, у меня такое ощущение, что мы говорим на разных языках ;) ;) и о разных вещах ???
Я говорю, что говнокод получился по причине чумового использования форычей, Вы мне отвечаете - индексы строятся быстро
Я говорю - нет смысла индексировать коллекцию для обхода её форычем, Вы мне отвечаете - так из нее можно отобрать нужные записи.
Как-то так :D :D
Понятно, что можно, но я-то о другом :P
-
Да, наверно о разном. Потому как я уже потерял нить рассуждений :)
-
Таки-да, сортировка стала быстрее и память вроде не жрёт. Набор из 3'786'524 записей был отсортирован за ~ 0:54 - 1:22 мин, разброс, видимо, связан с распределением памяти.
-
Наткнулся на примерно такой код для ЧЯ:
TYPE
Item = POINTER TO ItemDesc;
ItemDesc = RECORD
l, r : Item;
v : INTEGER;
END;
PROCEDURE Do* =
VAR
Comp: Item;
BEGIN
NEW ( Comp );
Comp.l := Comp;
Comp.r := Comp;
END;
Вопрос к знатокам ЧЯ, что произойдёт с экземпляром Item после выхода из Do(), как его GC обработает такие ссылки?
-
Вопрос к знатокам ЧЯ, что произойдёт с экземпляром Item после выхода из Do(), как его GC обработает такие ссылки?
Очень странный вопрос. В Блэкбоксе настоящий сборщик мусора, зацикленность ссылок ему глубоко безразлична.
-
Настоящий сборщик мусора это хорошо, но, вот есть, к примеру, двусвязный список, с элементами вышеуказанной структуры и что, в методе Clear этот список и расцеплять не надо? Почему там стоит только items := NIL?
PROCEDURE (l: List) Clear*;
BEGIN
l.items := NIL;
END Clear;
Если бы это был односвязный список, то всё понятно, этого достаточно, хотя я бы всё равно пробежался по списку и расцепил элементы - всё gc работы меньше - но список-то двусвязный, как gc эту гадость разрулит? Никак.
Вопросы возникли потому, что у клиента на ЧЯ крутится довольно серьезная вещь, за которую он когда-то заплатил немало зеленых рублей, но сейчас происходит перенос на альтернативную платформу и весь код приходится проверять на совместимость и адекватность.
-
Если бы это был односвязный список, то всё понятно, этого достаточно, хотя я бы всё равно пробежался по списку и расцепил элементы - всё gc работы меньше
Не факт. Это зависит от типа GC. Это было бы преждевременной оптимизацией иногда приводящей к излишним тормозам на пустом месте (удаление списка не O(1) а O(n)). А если список бесконечный…
-
Не факт. Это зависит от типа GC. Это было бы преждевременной оптимизацией иногда приводящей к излишним тормозам на пустом месте (удаление списка не O(1) а O(n)). А если список бесконечный…
Ну, если элементов миллионы, то, видимо, просядет существенно, с другой стороны, несвоевременное освобождение памяти занятой, по сути, мусорными объектами, тоже не есть гут, в любом случае, хоть ручное, хоть автоматическое разъединение такого количества элементов займет время. Другое дело, как его потратить.
Может вообще лучше вместо списков массивы использовать, так опять-же, если элементов миллионы и в массиве указатели, то всё равно ссылки инициализировать придётся.
Сергей Губанов, вроде, углубленно занимался изучением и ваянием GC, может он чего по этому поводу прояснит.
-
Настоящий сборщик мусора это хорошо, но, вот есть, к примеру, двусвязный список, с элементами вышеуказанной структуры и что, в методе Clear этот список и расцеплять не надо? Почему там стоит только items := NIL?
PROCEDURE (l: List) Clear*;
BEGIN
l.items := NIL;
END Clear;
Если бы это был односвязный список, то всё понятно, этого достаточно, хотя я бы всё равно пробежался по списку и расцепил элементы - всё gc работы меньше - но список-то двусвязный, как gc эту гадость разрулит? Никак.
Проблема, которую Вы имеете в виду, может случиться в каком-нибудь питоне, где вместо сборщика мусора идёт просто подсчёт ссылок. там да, зацикленный список может остаться мусором в памяти.
Сборщик мусора же смотрит, остались ли у самой программы ссылки на объект, если есть хоть одна живая ссылка, то объект остаётся в памяти, если же у программы живых ссылок уже нет (сделали l.items := NIL;), то этот объект является мусором и уничтожается сборщиком...
-
Если бы это был односвязный список, то всё понятно, этого достаточно, хотя я бы всё равно пробежался по списку и расцепил элементы - всё gc работы меньше - но список-то двусвязный, как gc эту гадость разрулит? Никак.
Алгоритм сборки мусора:
1) Пройтись по всем используемым блокам памяти и отметить их флагом "не используется".
2) Пройтись по всем достижимым указателям на объекты и перепометить блоки памяти в которых они размещены флагом "используется".
3) Утилизировать блоки памяти помеченные флагом "не используется".
Бегать по списку и вручную его расцеплять будет абсолютно бессмысленной тратой времени.
Однако есть одно исключение.
Одно исключение. Бывают консервативные сборщики мусора. Консервативный сборщик мусора не знает где лежат указатели, вместо этого любое целое число похожее на адрес объекта он на всякий пожарный случай трактует как указатель. Тогда да, для борьбы с фрагментацией может быть полезным врукопашную обнулять указатели везде где только можно.
Сборщик мусора Блэкбокса в куче работает точно, но консервативен на стэке. Поскольку в рассматриваемом примере список в куче, то в Блэкбоксе бессмысленно вручную расцеплять элементы двусвязного списка при удалении всего усписка.
-
Сергей Губанов, вроде, углубленно занимался изучением и ваянием GC
Да нет, я почти наоборот, я занимался тем как бы написать программу на C# так чтобы спрятать от GC объекты (на массивах структур), чтоб GC как можно меньше вреда причинял то и дело бегая по ним. Он же бегает только по указателям. Нет указателей - нет вреда от GC. Размещаешь объекты на массивах структур, а в роли указателей используешь индексы массивов, все дела.
-
Сборщик мусора Блэкбокса в куче работает точно, но консервативен на стэке. Поскольку в рассматриваемом примере список в куче, то в Блэкбоксе бессмысленно вручную расцеплять элементы двусвязного списка при удалении всего усписка.
Наконец-то, мои руки-ковырялки добрались до сборщика мусора. Ну что сказать, в результате экспериментов, я пришел к выводу, что в Оберонах (по крайней мере в имеющейся у меня версии компилятора/рантайма) имеет смысл для списков, массивов указателей и вообще для динамических(ссылочных) аккуратно обнулять указатели, у всех элементов. Т.е. ссылочные типы должны иметь финализаторы, в которых выполняется вся эта грязная работа по расщеплению списков, очистке массивов указателей и тд). Потому что это все равно придётся сделать. Не финализатору, так сборщику мусора. Но если мы это делаем в финализаторе, то сборка мусора становится более управляемой, ровной, не возникает коллапсов, за счет того, что сборщику мусора не приходится бегать по существенно меньшим трассам.
В общем, помогая сборщику мусора, мы помогаем себе )
-
Хотя, может я и не прав, пошел ещё тестировать
-
Не, я не прав. На второй системе результаты противоположны. Ну то есть сам сборщик работает как работал, а вот производительность в целом понизилась.
Теперь надо понять, почему в первом случае результат был положителен.
Хотя, возможно, где-то ошибка в реализации СМ, или какая-то специфика, но память здесь перераспределялась более оптимально.
-
Наконец-то, мои руки-ковырялки добрались до сборщика мусора. Ну что сказать, в результате экспериментов, я пришел к выводу, что в Оберонах (по крайней мере в имеющейся у меня версии компилятора/рантайма) имеет смысл для списков, массивов указателей и вообще для динамических(ссылочных) аккуратно обнулять указатели, у всех элементов. Т.е. ссылочные типы должны иметь финализаторы, в которых выполняется вся эта грязная работа по расщеплению списков, очистке массивов указателей и тд). Потому что это все равно придётся сделать. Не финализатору, так сборщику мусора. Но если мы это делаем в финализаторе, то сборка мусора становится более управляемой, ровной, не возникает коллапсов, за счет того, что сборщику мусора не приходится бегать по существенно меньшим трассам.
В общем, помогая сборщику мусора, мы помогаем себе )
Для чего нужен сборщик мусора? Именно для того что бы нам, простым пользователям компилятора не приходилось ручками занулять эти самые ссылки на списки, массивы и прочее -- пусть от этого болит голова сборщика мусора и его автора...