2014-09-08 5 views
0

У меня есть индекс, содержащий около 10 тыс. Элементов, которые необходимо отсортировать по регистру, лексикографически.Улучшить std :: sort performance

Это мой подход:

bool lowercomp (AbstractServiceProvider::AbstractItem* i, AbstractServiceProvider::AbstractItem* j) 

{ 
    std::string a,b; 

    // lower first string 
    a.resize(i->title().length()); 
    std::transform(i->title().cbegin(), i->title().cend(), a.begin(), 
       std::bind2nd(std::ptr_fun(&std::tolower<char>), std::locale(""))); 

    // lower 2nd string 
    b.resize(j->title().length()); 
    std::transform(j->title().cbegin(), j->title().cend(), b.begin(), 
       std::bind2nd(std::ptr_fun(&std::tolower<char>), std::locale(""))); 

    return 0 > a.compare(b); 
} 

Somwhere в моем коде:

t = new boost::timer::auto_cpu_timer; 
std::sort(_index.begin(), _index.end(), lowercomp); 
delete t; 

Но это занимает около 4 секунд. Без части toLower она занимает около 0,003 секунды. Есть ли способ улучшить это?

+0

Эта часть, я сомневаюсь. 'std :: sort' оптимизирован как есть. Любая дальнейшая оптимизация потребует скорее настраиваемого усовершенствованного обмана. – 101010

+1

Возможно, посмотрите на [сравнение нечувствительных к регистру строк в C++] (http://stackoverflow.com/questions/11635/case-insensitive-string-comparison-in-c)? – crashmstr

+0

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

ответ

4

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

  • ваша функция создает две новые строки при каждом вызове. Это может быть очень дорого, и

  • использовать два операнда форму std::tolower; эта функция должна извлечь ctype Facet каждый раз, когда она вызывается (и вы построить новый временный экземпляр локали каждый раз, когда вы вызова lowercomp.

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

class CaseInsensitiveCompare 
{ 
    std::locale myLocale; // To ensure lifetime of the facet. 
    std::ctype<char> const& myCType; 
public: 
    CaseInsensitiveCompare(std::locale const& locale = std::locale("")) 
     : myLocale(locale) 
     , myCType(std::use_facet<std::ctype<char>>(myLocal)) 
    { 
    } 
    bool operator()(AbstractItem const* lhs, AbstractItem const* rhs) const 
    { 
     return (*this)(lhs->title(), rhs->title()); 
    } 
    bool operator()(std::string const& lhs, std::string const& rhs) const 
    { 
     return std::lexicographical_compare(
      lhs.begin(), lhs.end(), 
      rhs.begin(), rhs.end(), 
      *this); 
    } 
    bool operator()(char lhs, char rhs) const 
    { 
     return myCType.tolower(lhs) < myCType.tolower(rhs); 
    } 
}; 

Помимо этого, есть несколько других моментов, которые могли бы улучшить производительность:

  • Если вы» убедитесь, что срок службы locale вы используете (и вы обычно можете это сделать), отбросьте член myLocale в класс ; копирование языка будет самой дорогой частью копирующих экземпляров этого класса (а звонок lexicographical_compare скопирует его хотя бы один раз).

  • Если вам не нужны функции локализации, рассмотреть возможность использования функции tolower в <cctype>, а не один в <locale>. Это позволит избежать необходимости в любых элементах данных вообще в сравнении.

  • Наконец, хотя я не уверен, что стоит что-то как маленьких, как 10K элементов, вы могли бы рассмотреть векторы с каноническими формами струн (уже ниже обсаженных и т.п.), сортировкой тех, используя только < на строках, а затем переупорядочивая оригинальные векторы в соответствии с этим.

Кроме того, я очень подозрительно относится к `новой подталкивание :: таймера :: auto_cpu_timer. Вам действительно нужно динамическое распределение ? С другой стороны, я подозреваю, что локальная переменная будет более подходящей.

+0

Ничего себе! Где я могу больше узнать об этом способе сравнения. Каковы соответствующие ключевые слова для такого рода «сравнительный класс»? – ManuelSchneid3r

+0

Мне не известны статьи, описывающие вышеизложенное; Я просто отработал, когда у меня возникла аналогичная проблема (сравнивая пути к файлам, где каждый путь был вектором строк). Имена файлов Windows не учитывают регистр, поэтому мне пришлось сравнивать строки). Существует множество статей, рекомендующих функциональные объекты вместо функций, но для остальных я в значительной степени сам придумал это. –

4

Вы можете определенно сделать это намного быстрее. Решение состоит в том, чтобы избежать выделения памяти и вместо этого сравнивать строки без учета регистра, преобразовывая один символ за раз, используя tolower() при выполнении сравнения. В функции сравнения не должно быть построено объектов класса. Что-то вроде этого:

bool lowercomp(const AbstractItem* lhs, const AbstractItem* rhs) 
{ 
    size_t size = std::min(lhs->title().size(), rhs->title().size()); 
    for (size_t pos = 0; pos < size; ++pos) { 
     if (tolower(lhs->title()[pos]) < tolower(rhs->title()[pos]) { 
      return true; 
     } else if (tolower(lhs->title()[pos]) > tolower(rhs->title()[pos]) { 
      return false; 
     } 
    } 
    return lhs->title().size() < rhs->title().size(); 
} 

Сообщите нам, как быстро это происходит. :)

+3

'size_t size = std :: max (...)' приводит к нарушению доступа, исправляется 'std :: min' – BeyelerStudios

+0

@BeyelerStudios: Действительно! Спасибо за исправление, глупая ошибка с моей стороны. –

+0

Согласен. Попробуйте написать функцию сравнения, которая непосредственно работает на i-> title(). Если можно, попробуйте передать AbstractItems по ссылке const. – midor

0

Ваша реализация поражает меня как чрезвычайно неэффективную. Я вижу несколько проблем.

Вы выполняете команду tolower на обеих строках в пределах сортировщик. Поскольку этот компаратор будет вызываться по порядку n log n раз, вы будете toloweringдва строк около 40 тыс. Раз (?) Каждый.

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

Если, однако, вы должны сравнить строки, скраб их до Вы выполняете сортировку. Это включает в себя tolower их. В идеале скраб данных при построении элементов. Если вы не согласны с этим, вы можете даже вычистить его перед тем, как позвонить sort. Независимо от того, что вы делаете, не чистите его в компараторе.

+0

Недостаточно вероятно, что tolower() ing будет самой важной проблемой с производительностью - построение большого количества std :: строк и std :: locales. –

+0

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

+0

@midor Scrubbing стоит усилий, когда задействовано много элементов. Но действительно ли 10K? –

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