Медленный перебор клеток - как ускорить?

  • Автор темы Автор темы PICC
  • Дата начала Дата начала
Статус
Закрыто для дальнейших ответов.

PICC

Участник
Топикстартер
Сообщения
16
Реакции
0
Доброго времени суток!

Я в скриптинге для InDesign новичок, так что не бейте сильно ...
Есть такой несложный скрипт, который в выделенных рядах устанавливает для четных ячеек один стиль, для нечетных - другой:

Код:
var sels = app.selection;
var styleOdd = "Белый";
var styleEven = "Серый";
var odd = 1;

try
{
    if (!sels.length)
        throw ("Необходимо что-нибудь выделить.");
    if (sels[0].constructor.name != "Cell")
        throw ("Необходимо выделить клетки. Сейчас выделено \"" + sels[0].constructor.name + "\".");
        
    for (i = 0; i < sels[0].rows.length; ++i)
    {
        var row = sels[0].rows[i];
        for (j = 0; j < row.cells.length; ++j)
        {
            var cell = row.cells[j];
            cell.clearCellStyleOverrides (true);
            cell.appliedCellStyle = odd ?  styleOdd : styleEven;
        }
        odd = 1 - odd;
    }
}
catch  (err)
{
    alert (err);
}

Работает правильно, но МЕДЛЕННО! Вручную я с такой же скоростью могу это сделать, если не быстрее. Машина двухядерная, с 4Гб памяти, WinXP, Adobe InDesign CS3.

Пытаюсь сделать перебор быстрее:

Код:
..
    with (sels[0].rows.everyItem ())
    {
        with (everyItem ())
        {
            clearCellStyleOverrides (true);
            appliedCellStyle = odd ?  styleOdd : styleEven;
        }
        odd = 1 - odd;
    }
..

И получаю ошибку.

Насколько я понял, я получаю массив, но я не могу работать с отдельными элементами массива - так, что ли?..
Подскажите, пожалуйста, а то уже час бьюсь!
 
Ответ: Медленный перебор клеток - как ускорить?

для четных ячеек один стиль, для нечетных - другой
Точно речь о ячейках, а не о строках?
У меня (ХР, ЦС3, 2ГБ) Ваш скрипт (верхний) отработал довольно шустро (правда, на пустой таблице в новом документе, 20х20 ячеек), и покрасил именно строки через одну.
 
Ответ: Медленный перебор клеток - как ускорить?

Я согласен - речь идет именно о строках, но работаю я на уровне ячеек - в общем-то, из-за того, что я не нашел способа присвоить стиль ячейки всей строке сразу.
Таблица у меня большая - на 64 стр. с кучей графики. Файл весит за сотню Мб. Поэтому там все медленно :-( ...

Насколько я понимаю, применение with для массива - это работа со всеми элементами сразу, что ли? Т. е. это не перебор элементов?
 
Ответ: Медленный перебор клеток - как ускорить?

применение with для массива - это работа со всеми элементами сразу
Нет. Для этого есть everyItem():
Код:
sels[0].rows[i].cells.everyItem().appliedCellStyle = odd ?  styleOdd : styleEven;

Попробуйте этой строкой заменить вложенный цикл (который перебирает ячейки в строке). Интересно, как отразится на скорости.
 
Ответ: Медленный перебор клеток - как ускорить?

Да, стало быстрее (что касается обработки всех ячеек в одной строке). Но все равно это медленно (а было МЕДЛЕННО :)).
Боюсь, что все операции с массивом допускаются именно одинаковые для всех элементов. А тут у меня - чередующиеся строки, все-таки не то. Так что, наверное, быстрее не станет...
Для интересующихся - текущий результат:

Код:
var sels = app.selection;
var styleOdd = "Белый";            // стиль нечетного ряда - 1, 3, 5, ...
var styleEven = "Серый";        // стиль четного ряда - 2, 4, 6, ...
var odd = 1;

// . выделено ли то, что надо?
try
{
    if (!sels.length)
        throw ("Необходимо что-нибудь выделить.");
    if (sels[0].constructor.name != "Cell")
        throw ("Необходимо выделить клетки. Сейчас выделено \"" + sels[0].constructor.name + "\".");
        
    for (i = 0; i < sels[0].rows.length; ++i)
    {
        var row = sels[0].rows[i];
        row.cells.everyItem().appliedCellStyle = odd ?  styleOdd : styleEven;
        row.cells.everyItem().clearCellStyleOverrides (true);
        odd = 1 - odd;
    }
}
catch  (err)
{
    alert (err);
}
 
Ответ: Медленный перебор клеток - как ускорить?

А просто свойствами таблицы сделать эту задачу нельзя?
Есть же Table Options - Fills к примеру.
 
Ответ: Медленный перебор клеток - как ускорить?

В случае, если таблица - это просто прямоугольная матрица, то без проблем. Но у меня в таблице есть объединенные ячейки, и у них форматирование другое (я не проверял, что произойдет в данном случае с использованием свойства Fills, сказать честно). Более того, мне нужно применять чередование для заданного диапазона строк.
 
Ответ: Медленный перебор клеток - как ускорить?

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

Код:
{
        var row = sels[0].rows[i];
        if (!odd){
        row.cells.everyItem().appliedCellStyle =  styleEven;
        row.cells.everyItem().clearCellStyleOverrides (true);}
        odd = 1 - odd;
    }
Не ручаюсь за правильность и работоспособность, только на уровне концепта.
 
Ответ: Медленный перебор клеток - как ускорить?

Сначала всем ячейкам в выделении применяем белый стиль, а в цикле присваиваем только серый
Прикрутил счетчик времени, замерял оба варианта. Первый (изначальный) оказался быстрее. :)
Как вариант, можно еще руками (через горячую кнопку) назначать сначала "белый", а скриптом через одну - "серый", ну это уже извраще дело вкуса. Да и проверять конечно надо на серьезном объеме.
 
Ответ: Медленный перебор клеток - как ускорить?

Код #5 уже очень хорош. Еще 20-30% можно выжать убрав за цикл clearCellStyleOverrides, отключив перерисовку окна на время выполнения и кешируя свойства объектов в переменных:

Код:
var timeStart = new Date().getTime();


var sels = app.selection;
var styleOdd = "Белый";            // стиль нечетного ряда - 1, 3, 5, ...
var styleEven = "Серый";           // стиль четного ряда - 2, 4, 6, ...

// выделено ли то, что надо?
if (!sels.length || sels[0].constructor.name != "Cell") {
    alert ("Необходимо выделить ячейки таблицы");
    exit();
}
// выключим перерисовку окна во время выполнения скрипта 
var enableRedraw = app.scriptPreferences.enableRedraw;
app.scriptPreferences.enableRedraw = false;
var rows = sels[0].rows;

// назначим ячйкам стиль
for (var i = rows.length; i--;) {
    rows[i].cells.everyItem().appliedCellStyle = 
    	i % 2 ? styleEven : styleOdd;
}
// сбросим переопределения ячеечного стиля
sels[0].cells.everyItem().clearCellStyleOverrides(true);
app.scriptPreferences.enableRedraw = enableRedraw;


alert((new Date().getTime() - timeStart) / 1000 + 'c');
 
Ответ: Медленный перебор клеток - как ускорить?

Попробовал:
- убрать за цикл - полезно;
- отключение перерисовки - скорость, как по мне, СУЩЕСТВЕННО не увеличило, зато полностью убрало наглядность - т. е. я не вижу, что происходит сейчас;
- кеширование - это как (в данном случае)?
- for ( var i = rows.length; i--; ) - зачем так хитро :) ?

Кстати, переменную odd я ввел не случайно (вместо i % 2) . Оно позволяет определять "четность", "нечетность" безотносительно переменной i - иногда первый выделенный д. б. белым, иногда - серым. И изменение значения при определении odd позволяет делать это.
 
Ответ: Медленный перебор клеток - как ускорить?

for ( var i = rows.length; i--; ) - зачем так хитро ?

=) хо-хо! этой строчке не меньше лет этак 30-и! читайте классиков. Ее прелесть в краткости и в том что обращение к свойству length массива rows выполняется только один раз. Помните: операции с встроенными примитивами JS выполняются в сотни раз быстрее быстрее, чем обращения к DOM.

кеширование - это как

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

Код:
styleEven = app.activeDocument.cellStyles.itemByName(styleEven);
styleOdd = app.activeDocument.cellStyles.itemByName(styleOdd);

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

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

отключение перерисовки - скорость, как по мне, СУЩЕСТВЕННО не увеличило, зато полностью убрало наглядность

Лучше будет во время выполнения показывать "градусник"
 
Ответ: Медленный перебор клеток - как ускорить?

Спасибо за интересные идеи! В Си я программирую не один десяток лет (С++ - поменьше), но, собственно, "классиков" не читал...
Да, за тормазнутость обращения к DOM я тоже заметил. Странно - почему веб-броузеры не тормозят при этом, а InDesign просто задыхается???

Лучше будет во время выполнения показывать "градусник"
А как это делается?

Я попробовал внести кеширование - скорость если и выросла, то я этого не заметил...
Что-то меня быстродействие InDesign при работе с JavaScript расстраивает... Вроде бы там можно подключать внешние PlugIn-ы? Я бы тогда написал код на С++, он бы, я думаю, на порядок шустрее бы работал. Хотя там тоже есть, небось, свой DOM, и он будет также тормозить ':('
 
Ответ: Медленный перебор клеток - как ускорить?

Странно - почему веб-броузеры не тормозят при этом, а InDesign просто задыхается???

Конкуренция: браузеры уже не один год соревнуются друг с другом в скорости. А в InDesign хорошо что такое есть.

Лучше будет во время выполнения показывать "градусник"
А как это делается?

Поищите файл InDesignCS3_ScriptingGuide_JS.pdf Там есть пример "Creating a Progress Bar with ScriptUI".

быстродействие InDesign при работе с JavaScript расстраивает

Мой старенький Macbook MA699LL обрабатывает таблицу 100 строк по 100 ячеек в каждой строке (10000 ячеек!) ровно одну минуту. По-моему получилось очень неплохо. Как быстро вы бы хотели?

Вроде бы там можно подключать внешние PlugIn-ы?

"Можно" это не то слово. InDesign весь насквозь скроен из плагинов как лоскутное одеяло. Но я что-то не припоминаю ни одной практической задачи чтобы был смысл связываться с написанием своего. Например, если вы хотите чтобы в импортируемом куске текста сразу были размечены нужные стили, то самое простое предварительно подготовить его используя Tagged Text. tagged_text.pdf
 
Ответ: Медленный перебор клеток - как ускорить?

А в InDesign хорошо что такое есть.
И то правда!

Мой старенький Macbook MA699LL обрабатывает таблицу 100 строк по 100 ячеек в каждой строке (10000 ячеек!) ровно одну минуту. По-моему получилось очень неплохо. Как быстро вы бы хотели?
Ну... В моем понимании двухъядерный компьютер с 3 ГГц и 4 Гб должен обрабатывать такое за секунду или доли оной!
Понятное дело - если на каждое обращении к DOM тратить неоправданно много кода - работать будет медленно. И поэтому будет стимул покупать все новые и новые версии InDesign, Windows и "железа".
Или уже ушли в прошлое оптимально построенные программы?..

...самое простое предварительно подготовить его используя Tagged Text. tagged_text.pdf
Кстати говоря, это поддерживается в CS3? Или только в 5.5?
 
Ответ: Медленный перебор клеток - как ускорить?

должен обрабатывать такое за секунду или доли оной!

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

самое простое предварительно подготовить его используя Tagged Text. tagged_text.pdf
Кстати говоря, это поддерживается в CS3? Или только в 5.5?

Файл по ссылке озаглавлен как TAGGED TEXT USER GUIDE ADOBE INDESIGN CS3

Стало интересно насколько силен эффект от кеширования свойств. В результате родился этюдик:

Код:
var 
	timer1 = 0,
	timer2 = 0,
	startTime;

// вызов по цепочке
startTime = new Date();

for (var i = 9999; i--;) {
	var label = app.activeDocument.paragraphStyles.item(0).fillColor.label;
}
timer1 += new Date().getTime() - startTime.getTime();

// вызов по ссылке
var link = app.activeDocument.paragraphStyles.item(0).fillColor;
startTime = new Date();

for (var i = 9999; i--;) {
	var label = link.label;
}
timer2 += new Date().getTime() - startTime.getTime();

alert('Вызов по ссылке быстрее в ' + Math.round(timer1/timer2) + ' раз');

Получилось, что 5 уровней вложенности замедляют вызов в 18 раз.
 
Ответ: Медленный перебор клеток - как ускорить?

Стало интересно насколько силен эффект от кеширования свойств. В результате родился этюдик:

Хе-хе. Переписал Ваш код на С++, увеличил число в цикле в 100 раз. Результат - в 500 раз.
Общее время исполнения около 1 сек.

rdtsc - возвращает кол-во тиков процессора с момента запуска, так что считал не время, а кол-во прошедших тиков.

Код:
IDocument *theDoc = ac->GetContextDocument();
	
	long long start1 = rdtsc();
	
	for(int i = 999999; --i;)
	{
		InterfacePtr<IStyleGroupManager> pIStyleGroupManager(theDoc->GetDocWorkSpace(), IID_IPARASTYLEGROUPMANAGER);
		IStyleGroupHierarchy *rootHierarchy = pIStyleGroupManager->GetRootHierarchy();
		InterfacePtr<IStyleGroupHierarchy> firstChild(rootHierarchy->QueryChild(0));
		InterfacePtr<IStyleInfo> styleInfo(firstChild, UseDefaultIID());
		styleInfo->GetName();
	}
	long long end1 = rdtsc();
	

	InterfacePtr<IStyleGroupManager> pIStyleGroupManager(theDoc->GetDocWorkSpace(), IID_IPARASTYLEGROUPMANAGER);
	IStyleGroupHierarchy *rootHierarchy = pIStyleGroupManager->GetRootHierarchy();
	InterfacePtr<IStyleGroupHierarchy> firstChild(rootHierarchy->QueryChild(0));
	InterfacePtr<IStyleInfo> styleInfo(firstChild, UseDefaultIID());

	long long start2 = rdtsc();
	
	for(int i = 999999; --i;)
	{
		styleInfo->GetName();
	}
	long long end2 = rdtsc();
	
	
	long long time1 = end1 - start1;
	long long time2 = end2 - start2;
	
	PMString number;
	number.AsNumber(time1/time2);

	CAlert::InformationAlert(number);
 
Ответ: Медленный перебор клеток - как ускорить?

Я так понимаю, это и есть кусок плагина на С++?

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

Я так понимаю, это и есть кусок плагина на С++?

RDTSC в данном случае, я думаю, не совсем корректен, т. к. происходит масса всяких посторонних вызовов - я имею в виду, множество запущенных процессов.
Согласен. Просто остальные таймеры дают точность в милисекундах, при этом тоже происходит масса посторонних вызовов.
Повторил используя mach_absolute_time(). Результат – ~600 раз.
 
Статус
Закрыто для дальнейших ответов.