2009-06-10 6 views
9

Одним из улучшений C++ 0x, которые позволят написать более эффективный код на C++, является интеллектуальный указатель unique_ptr (слишком плохо, что он не позволит перемещаться по memmove() как операции: предложение не внесены в проект).Улучшения производительности C++ 0x

Каковы другие улучшения производительности в предстоящем стандарте? Возьмем следующий код, например:

vector<char *> v(10,"astring"); 
string concat = accumulate(v.begin(),v.end(), string("")); 

код будет сцеплять все строки, содержащиеся в векторе v. Проблема с этой аккуратной частью кода заключается в том, что accumulate() копирует вещи вокруг и не использует ссылки. И строка() перераспределяет каждый раз, когда вызывается оператор. Таким образом, код имеет низкую производительность по сравнению с хорошо оптимизированным аналоговым кодом C.

Предоставляет ли C++ 0x инструменты для решения проблемы и, возможно, другие?

+1

Вот еще один ответ по теме: http://stackoverflow.com/questions/637695/how-efficient-is-stdstring-compared-to-null-terminated-strings/637737#637737 –

ответ

13

Да C++ решает проблему через что-то называемое перемещение семантики.

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

Это делается путем введения конструктора перемещения . Его конструктор, где вы знаете, что объект src является временным и уходит. Поэтому для получателя приемлемо принимать внутреннее представление объекта src.

То же самое верно для Операции назначения переезда.

Чтобы отличить конструктор копирования от конструктора перемещения, язык ввел ссылки rvalue. Класс определяет свой конструктор перемещения, чтобы получить ссылку rvalue, которая будет привязана только к rvalues ​​(temporaries). Так что мой класс будет определить что-то вдоль линий:

class CMyString 
{ 
private: 
    char* rawStr; 
public: 

    // move constructor bound to rvalues 
    CMyString(CMyString&& srcStr) 
    { 
     rawStr = srcStr.rawStr 
     srcStr.rawStr = NULL;    
    } 

    // move assignment operator 
    CMyString& operator=(CMyString&& srcStr) 
    { 
     if(rawStr != srcStr.rawStr) // protect against self assignment 
     { 
      delete[] rawStr; 
      rawStr = srcStr.rawStr 
      srcStr.rawStr = NULL; 
     } 
     return *this; 
    } 

    ~CMyString() 
    { 
     delete [] rawStr; 
    } 
} 

Here очень хорошая и подробная статья по ходу семантики и синтаксиса, что позволяет сделать это.

+3

Это делает операции перестановки как сортировка и вращение коллекций коллекций намного быстрее. Я подозреваю, что наиболее вероятным примером будет сортировка коллекции строк. – Brian

+1

Это также очень хорошее объяснение ссылок rvalues: https://www.boostpro.com/trac/wiki/BoostCon09/RValue101 –

+0

Расширение (или преобразование) всех контейнеров и алгоритмов для перемещения семантики выглядит как большая задача, интересно, как происходит ситуация, и если совместимость с фактическим STL будет сохранена без эффективности торговли. – 2009-06-10 22:36:47

7

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

constexpr int returnSomething() {return 40;} 

int avalue[returnSomething() + 2]; 

Это не законный код на C++, поскольку returnSomething() + 2 не является постоянным выражением.

Но, используя ключевое слово constexpr, C++ 0x может сообщить компилятору, что выражение является константой времени компиляции.

+1

Еще словари! У нас уже есть volatile, unsigned, restrict, static, const, Register ... am missing no? –

+1

@TrevorBoydSmith: C++ имеет 60 ключевых слов, вам не хватает тонны –

1

Извините - вы не можете утверждать как факт, что string concat = accumulate(v.begin(),v.end(), string(""));должно перераспределить. Разумеется, простая реализация. Но компиляторам очень разрешено делать правильные вещи здесь.

Это уже имеет место в C++ 98, а C++ 0x продолжает использовать как умные, так и немые реализации. Тем не менее, перемещение семантики упростит интеллектуальные реализации.

+0

Здесь скопировано() тело из gcc 4.1.1: for (; __first! = __last; ++ __ first) __init = __init + * __ first; return __init; Вы правы говоря, что реализациям разрешена оптимизация - путем предоставления специализаций. Для строки это частично возможно, специализация должна: 1) взять строку в качестве ссылки или указателя - это сложно, потому что подпись функции указана стандартом 2) использовать append() или + = - это возможно. Но ведь я думаю, что правильные новые инструменты на языке позволят решить проблему без особых специализаций. – 2009-06-10 15:36:23

0
vector<string> v(10, "foo"); 
string concat = accumulate(v.begin(), v.end(), string("")); 

Этот пример - просто плохое программирование на любом стандарте C++. Это эквивалентно следующему:

string tmp; 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 

C++ 11 двигаться семантика будет принимать только заботиться о «копировать результат обратно в TMP» части уравнения. Начальные копии от tmp все равно будут копиями. Это классический Schlemiel the Painter's algorithm, но еще хуже, чем обычно, например, с использованием strcat в С.

Если accumulate просто использовали += вместо + и = тогда это было бы избежать всех этих копий.

Но C++ 11 действительно дает нам возможность сделать лучше, оставаясь при этом лаконичным, используя функцию лямбды:

string concat; 
for_each(v.begin(), v.end(), [&](const string &s){ concat += s; }); 

EDIT: Я полагаю, стандартная библиотека реализатор может выбрать для реализации accumulate с ходом на операнд +, так что tmp = tmp + "foo" стал бы tmp = move(tmp) + "foo", и это решило бы эту проблему. Я не уверен, что такая реализация будет строго соответствовать. В настоящее время ни GCC, ни MSVC, ни LLVM не делают этого. А так как accumulate определен в <numeric>, можно предположить, что он предназначен только для использования с числовыми типами.