2010-06-22 7 views
8

Я планирую написать интерактивный плагин обработки геометрии C++, который будет часто сортировать большие объемы данных. Хотя предварительные указания состоят в том, что сортировка займет всего секунду или два, я бы предпочел показать прогресс за это время - я бы хотел обновить индикатор прогресса несколько раз в секунду. Это было бы предпочтительнее включить курсор ожидания и оставить пользователя с программой замерзает в течение неопределенного промежутка времени (даже если это всего лишь несколько секунд).Как отслеживать/показывать прогресс во время сортировки по C++

Если бы я использовал что-то вроде std :: sort, я мог бы использовать функцию сравнения, чтобы обновлять индикатор прогресса время от времени, но я бы не имел понятия «процент завершен». Я мог бы также разбить сортировку на подтипы, обновить прогресс между подтипами и затем слить. Мое лучшее решение может заключаться в том, чтобы написать собственный метод сортировки, хотя я не знаю, сколько усилий потребуется для достижения производительности так же, как std :: sort (и обеспечить правильность). В любом случае этот метод сортировки иногда посылает «процентное завершение» методу обратного вызова.

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

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

Обновление 2: Я выполнил свой проект, и это оказалось не проблемой (по крайней мере, для клиента. Поскольку они будут продавать программное обеспечение, они могут по-прежнему получать отзывы от своих клиентов, которые будут меняться их умы об этом). Выбор приемлемого ответа был затруднен, потому что было много хороших ответов, но в итоге тот, который я выбрал, указывал на статью wiki по Merge Sort, которая имела очень запоминающуюся анимацию. Итак, это первая стратегия, которую я бы преследовал, если бы мне нужно было продолжить это).

+3

Лично я бы отложил добавление такой функции, пока не будет наблюдаться реальная производительность сортировки. В противном случае он решает проблему, которая может не существовать. Вы также можете проделать простой маршрут и отобразить «Сортировка ...» в виде текстового элемента управления или в строке состояния. – Reinderien

+1

@Reinderien: согласился, если он не сломался, не исправить это. Но я пытаюсь задуматься об этом. И мой опыт в области 3D-графики и обработки геометрии заключается в том, что пользователи будут легко задушить что-либо с моделями и данными, большими, чем вы когда-либо мечтали. – brainjam

ответ

4

Поскольку std :: sort основан на шаблоне, источник должен быть доступен в заголовке. Вы можете сделать копию и вставить обратный вызов прогресса. Большая проблема будет предсказывать, насколько вы близки к завершению - большинство функций будут основаны на Quicksort, что не всегда делает то же самое количество сравнений.

Дать объявление самостоятельно Merge sort было бы возможно; алгоритм прост и количество шагов хорошо определено.

+0

Оба хороших предложения. Мне не приходило в голову, что std :: sort был основан на шаблонах. Для дальнейшего использования существует реализация C++ для сортировки Merge на rosettacode.org: http://rosettacode.org/wiki/Merge_sort#C.2B.2B – brainjam

2

Я бы порекомендовал ваш второй вариант: используйте std::sort или другую стандартную функцию сортировки, например qsort, и сообщите о ее прогрессе. Но не обновляйтесь при каждом сравнении - это будет невыносимо slow - вместо этого обновите каждые (скажем) 100 мс.

+1

Это не отвечает на большой вопрос OPs.Как вы на самом деле выясните, как далеко продвинулся этот вид, используя этот метод? – Omnifarious

+1

Я думаю, что если вы дадите компаратору размер массива в его конструкторе, а затем примените аппроксимацию Omifarious выше (что будет сравнение (n lg n)). Затем компаратор мог отслеживать, сколько раз он был вызван. Я не уверен и не до конца продумал это, но я думаю, что слияние может поддаваться хорошему отслеживанию прогресса. Но, конечно, слияние сортировки не интросортировано. Все еще слияние сортировки (n lg n) и может быть приемлемым. –

+0

@Craig W. Wright: Это было бы сложно, потому что функциям сравнения STL не разрешено иметь состояние. –

0

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

9

Я думаю, даже если вы написали свой собственный вид, что вам нужно будет сделать много тщательного измерения, если вы хотите, чтобы индикатор прогресса был точным. Если вам нужен только приблизительный индикатор прогресса, то вы можете использовать некоторую метрику, такую ​​как «среднее расстояние между сравниваемыми элементами» или «количество сравнений по сравнению со средним ожидаемым числом для быстрой сортировки» в качестве вашей метрики и реализовать идею сравнения, о которой вы уже упоминали.

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

В качестве примера вы обычно ожидаете от n log2 n операций для быстрой сортировки. Анализ количества сравнений является более подробным и может быть более точным, чем эта общая мера, но для целей этого примера давайте просто предположим. Таким образом, вы можете рассчитывать на сопоставления и сообщать number_of_comparisons/(n log2 n) как свою оценку прогресса.

Поскольку это всего лишь средний показатель, я бы провел несколько экспериментов и посмотрел, насколько далеко ваша оценка выключена, и добавьте некоторые факторы выдумки, чтобы они совпадали со средним ожидаемым случаем. У вас также может быть индикатор выполнения, который указывал на неопределенность, имея вид «Вот где я думаю, что я закончу». индикатор и некоторое пространство после индикатора.

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

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

Если вы хотите использовать свой собственный вид и предсказуемость, это ваша самая высокая цель, зайдите на heapsort. Он по-прежнему относится к типу O(n log2 n), и он близок к минимальной сортировке (или я помню, что читал Кнут). Также требуется очень предсказуемое количество времени для завершения, независимо от того, какой набор данных он кормил. Это один из медленных сортов O(n log2 n), но все же.

Как отметил один из ваших комментаторов, вы можете решить проблему, которая на самом деле не существует. Сначала проведите несколько экспериментов. Однако проблема - интересная интеллектуальная проблема, независимо от ее полезности. :-)

+0

+1 для размышлений о том, как измерить прогресс. Если бы я собирался написать свой собственный, мне все равно пришлось бы это выяснить. Я предполагаю, что реальный вопрос заключается в том, насколько я знаю знание внутреннего состояния алгоритма, а не только количество сравнений до сих пор. И спасибо, если предположить, что я не полный идиот об обновлении индикатора прогресса за каждым сравнением, хотя вы можете смело предположить, что я полный идиот о сортировке. – brainjam

+0

@brainjam: Я не специалист по алгоритмам, но из того, что знаю, зная, что внутреннее состояние не покупает вам столько полезных данных, как вы могли бы подумать. Например, Quicksort может занять очень мало времени для одной стороны и очень длительное время для другого после того, как список будет разделен пополам. И если вы выберете предсказуемую сортировку, вы можете предсказать количество операций сравнения так же легко, как и сколько времени выполнить различные подзадачи. – Omnifarious

+0

Точность в индикаторе прогресса не так важна, как держать пользователя развлекаемым во время простоя, устанавливая их ожидания и позволяя им отменить. Поэтому я думаю, что я просто удвоил оценку до '2 * n * log2 (n)', и если сортировка закончится быстрее, чем ожидалось, тем лучше. – brainjam

1

Я вижу вашу проблему как следующее:

  1. Вы хотите дискретные события, которые будут уволены в течение одного непрерывного процесса.
  2. Это подразделение предназначено только для того, чтобы сообщить, что пользователь находится в процессе.

Мои предложения являются:

  1. Используйте значок загрузки из чего-то вроде http://ajaxload.info/, или если это не среду графического интерфейса на основе, просто сформулировать нагрузку. Поскольку это событие меньше 2 секунд, это не будет проблемой. Ожидается, что время ожидания превышает 10 секунд.

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

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

1

использование перебор :)

int elem_num = raw_data.size(); 
int percentage_delta = 100/(elem_num/20); 
int percentage = 0; 
int i = 0; 
std::multiset<Elem*> sorted_data(&compareElemFunc); 
foreach(Elem& elem, raw_data) 
{ 
    sorted_data.insert(&elem); 
    if(i%20) 
    { 
     updateProgressBar(percentage); 
     percentage += percentage_delta; 
    } 
    i++; 
} 
//now, your data is perfectly sorted, iterate through sorted_data 

(в случае, если вы не хотите, чтобы реализовать свой собственный зЬй :: сортировки() и так как я не хватает полных требований)

+0

Я полагаю, что это O (n logn), но мне интересно, как это сравнивается к выполнению std :: sort. Если std :: sort занимает 1 секунду, и это решение занимает 10 секунд, я бы дважды подумал об использовании его. Самое приятное в этом решении заключается в том, что вы можете отменить процесс в любое время. Кстати, я бы изменил коэффициент обновления хода от 20 до 1000 или даже 10000 - достаточно нескольких обновлений в секунду. – brainjam

0

Я не» Рекомендуем попробовать взломать std :: sort. Это обычно реализуется с помощью интросорта и является чрезвычайно быстрой операцией NLogN. Построение контейнера, который вы собираетесь сортировать, обычно будет дороже, чем сортировка данных.

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

Вы можете обобщить этот неинтрузивный подход к любой длительной операции без необходимости разбрасывать вызовы типа type_progress_bar() по всему вашему коду или копаться в реализациях std :: sort или пытаться изобрести колеса. Поскольку основной поток будет находиться в состоянии ожидания/обновления состояния выполнения и, следовательно, блокируется в некотором смысле, пока ваш рабочий поток не будет завершен, у вас нет проблем, связанных с многопоточным (необходимость синхронизации потоков для доступа к общим ресурсам на протяжении всего вашего приложение, за исключением счетчика прогресса, условий гонки, мертвых замков и т. д.). Это также будет самый гладкий счетчик прогресса, который вы можете реализовать, так как он будет обновляться одновременно.

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

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

Также, если ваш introsort занимает так много времени, я должен задаться вопросом, хранит ли ваш контейнер эти объекты треугольника или указатели на них? Если первое, вы можете рассмотреть последнее, так как оно должно ускорить процесс.

Смежные вопросы