строка [x] против * строка ++ - PullRequest
7 голосов
/ 08 ноября 2010

Какой из 2 методов теоретически быстрее и почему? (указатель на строку должен быть постоянным.)

Какова точная разница между пункт назначения [количество] и * пункт назначения ++ ? Продолжает ли пункт назначения [count] переходить с 0 на count при каждом вызове? * destination ++ просто добавляет 1 при каждом вызове?

char *const string = "Hello world!";
char *destination = malloc(strlen(string) + 1);
int count = 0;
while(string[count] != '\0')
{
    destination[count] = string[count];

    count++;
}

char *const string = "Hello world!";
char *destination = malloc(strlen(string) + 1);
char *ptr = string;
while(*ptr != '\0')
{
    *destination++ = *ptr++;
}

Ответы [ 8 ]

6 голосов
/ 08 ноября 2010

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

При этом destination[count] делает не цикл от 0 до count при каждом вызове.Он занимает ячейку памяти destination[0], добавляет count размер, умноженный на тип *destination, и ищет там.(Конечно, все это наивно: компилятор, вероятно, делает что-то быстрее.)

С другой стороны, *destination++ занимает место в памяти destination[0] (то есть , а не ).начало массива больше, так как вы изменили destination), смотрит туда и добавляет размер типа *destination, так что destination[0] относится к тому, что раньше было destination[1].

5 голосов
/ 08 ноября 2010

Почему мы должны спекулировать? Мы можем попробовать и выяснить. Я скомпилировал код с gcc -O3 -g (на x86) и разобрал результат. Произошло больше изменений, чем я ожидал, поэтому я сосредоточусь на кусочках посередине, где мы ожидаем, что большая часть различий между ними будет. Ядро цикла в первом случае:

0x00000030 <foo+48>:    mov    %dl,(%edi,%esi,1)
0x00000033 <foo+51>:    movzbl 0x1(%ecx),%edx
0x00000037 <foo+55>:    inc    %eax
0x00000038 <foo+56>:    inc    %ecx
0x00000039 <foo+57>:    mov    %eax,%esi
0x0000003b <foo+59>:    test   %dl,%dl
0x0000003d <foo+61>:    jne    0x30 <foo+48>

Ядро цикла во втором случае:

0x00000080 <foo2+48>:   mov    %dl,(%eax)
0x00000082 <foo2+50>:   movzbl 0x1(%ecx),%edx
0x00000086 <foo2+54>:   inc    %eax
0x00000087 <foo2+55>:   inc    %ecx
0x00000088 <foo2+56>:   test   %dl,%dl
0x0000008a <foo2+58>:   jne    0x80 <foo2+48>

Исходя из этого, второе, возможно, немного быстрее. Но на самом деле, это не будет иметь большого значения на практике. Кэш L1 прекрасно держит оба цикла, а целевая память не кэшируется, поэтому различия будут спорными. Удачи в измерении разницы между ними.

5 голосов
/ 08 ноября 2010

В старые добрые времена второй метод работает быстрее.Но с современной оптимизацией компилятора, они точно такие же .

Это потому что:

destination[count] = string[count];

будет делать

*(destination+count) = *(string+count);

В каждом цикле требуется еще две операции add.Современный компилятор сделает это за вас.

2 голосов
/ 08 ноября 2010

Зависит от процессора.На x86 первая версия немного быстрее (или, по крайней мере, приведет к несколько более короткому коду), потому что для загрузки из string [count] требуется всего одна инструкция, а для записи в destination [count] - одну.Таким образом, псевдокод будет выглядеть примерно так (каждая строка = одна инструкция по сборке)

register = *(string+count) 
*(destination+count) = register
count += 1
compare register, 0
jump to line 1 if nonzero

, а вторая версия будет

register = *ptr
*destination = register
ptr += 1
destination += 1
compare register, 0
jump to line 1 if nonzero

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

0 голосов
/ 08 ноября 2010

Глядя на это с высокого уровня, обе операции предполагают разыменование и сложение.Выражение destination[count] оценивается как *(destination + count).Выражение *destination++ (наиболее вероятно) оценивается как *destination; destination += 1 (с учетом того, что побочный эффект необходимо применять только до следующей точки последовательности, что не обязательно сразу после оценки выражения).

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

0 голосов
/ 08 ноября 2010

Если «строка» является аргументом функции, вы можете использовать ее в качестве итератора вместо «ptr».Тогда у вас будет на одну переменную меньше, чтобы держать в стеке.Это может означать, что компилятор может хранить все локальные переменные в регистрах, и это повысит производительность.

Это, конечно, зависит, если компилятору удастся избавиться от «количества», вы не получите никаких приращений производительности.

0 голосов
/ 08 ноября 2010

destination[count] - это счет -ого элемента (на основе 0) в destination[].count++ гарантирует, что при каждом входе в цикл, destination[count] равен одному последнему элементу, к которому обращались в предыдущей итерации (кроме, конечно, первого входа).

*destination++ = *ptr++; совпадает сследующее:

*destination = *ptr;
destination++;
ptr++;

Обратите внимание, что destination++ увеличивает значение указателя на sizeof(*destination), которое будет указывать на следующий элемент в массиве, а не на данные, на которые указывает destination (или *destination).

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

0 голосов
/ 08 ноября 2010

*destination++ фактически добавляет sizeof(*destination) при каждом вызове (это зависит от размера указателя), в то время как destination[count] должно делать *(destination + count * sizeof(*destination)), но, вероятно, все равно оптимизировано компилятором.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...