Как стать автором
Обновить

Комментарии 26

Ну Go всё-таки не предназначен для расчетных задач и это не приоритет у разработчиков. Если вам очень хочется оптимизировать конкретный кусок, то лучше использовать встроенный ассемблер (но это непортируемое решение, конечно же)
> непортируемое
можно выехать за счет build tags
Можно, конечно, предсказать комментарий, вида «В три строки делаем HTTP сервер на Go. Си еще есть куда расти»)
Но ведь действительно, разве Go позиционируется, как замена Си, включая производительность?
Ну мне кажется любой язык компилируемый в оп кода процессора можно считать заменой Си
Уже наличие сборщика мусора в языке автоматически исключает возможность стать заменой языку Си. Также необходим zero runtime, т.е. возможность полного отказа от использования стандартной библиотеки. Иначе просто нереально будет писать ядра операционных систем или прошивки для микроконтроллеров. А это именно те области, где для Си нет альтернатив.
Тут я конечно не спорю, но иногда на Си можно написать так, что лучше бы был сборщик мусора

Можно, но совершенно незачем. Ни богатства в итоге, ни славы.

При жёстком недостатке памяти очень важно точно контролировать что когда там создаётся и освобождается.
Сборщик мусора при этом будет источником нескончаемой боли разработчика.
А написать так можно на любом языке.
НЛО прилетело и опубликовало эту надпись здесь

Некорректно приводить сравнения, в которых действия выполняются меньше секунды. А вдруг это просто система лагнула?

результат стабильный
О стабильности результата можно было бы говорить, если бы Вы привели ну там 1000 экспериментов, показали матожидание, дисперсию, расказали о своём подходе к минимизации посторонних факторов и т.д. А так тест выглядит почти как случайные данные.
Астрологи объявили месяц сравнения языков программирования?
Предлагаю скомпилировать сишный код с "-Ofast -march=native", ассемблерный код получится намного интереснее. Помимо этого, если у вас проц от Intel, имеет смысл скомпилить через icc.
тот же результат
0x0000555555554590 <+32>: add $0x1,%eax
0x0000555555554593 <+35>: pxor %xmm0,%xmm1
0x0000555555554597 <+39>: paddq %xmm2,%xmm0
0x000055555555459b <+43>: cmp $0x4c4b40,%eax

А у меня результат (gcc 8.2.1) выглядит так:


(gdb) disassemble main 
Dump of assembler code for function main:
   0x0000000000001040 <+0>:     push   %rbp
   0x0000000000001041 <+1>:     vmovdqa 0xfd7(%rip),%ymm0        # 0x2020
   0x0000000000001049 <+9>:     xor    %eax,%eax
   0x000000000000104b <+11>:    vpxor  %xmm3,%xmm3,%xmm3
   0x000000000000104f <+15>:    vmovdqa 0xfe9(%rip),%xmm4        # 0x2040
   0x0000000000001057 <+23>:    mov    %rsp,%rbp
   0x000000000000105a <+26>:    and    $0xffffffffffffffe0,%rsp
   0x000000000000105e <+30>:    xchg   %ax,%ax
   0x0000000000001060 <+32>:    vextractf128 $0x1,%ymm0,%xmm1
   0x0000000000001066 <+38>:    vpaddq %xmm0,%xmm4,%xmm2
   0x000000000000106a <+42>:    vpaddq %xmm1,%xmm4,%xmm1
   0x000000000000106e <+46>:    add    $0x1,%eax
   0x0000000000001071 <+49>:    vxorps %ymm0,%ymm3,%ymm3
   0x0000000000001075 <+53>:    vinsertf128 $0x1,%xmm1,%ymm2,%ymm0
   0x000000000000107b <+59>:    cmp    $0x2625a0,%eax
   0x0000000000001080 <+64>:    jne    0x1060 <main+32>
   0x0000000000001082 <+66>:    lea    0xf7b(%rip),%rdi        # 0x2004
   0x0000000000001089 <+73>:    vmovdqa %xmm3,%xmm0
   0x000000000000108d <+77>:    xor    %eax,%eax
   0x000000000000108f <+79>:    vextractf128 $0x1,%ymm3,%xmm3
   0x0000000000001095 <+85>:    vpxor  %xmm3,%xmm0,%xmm3
   0x0000000000001099 <+89>:    vpsrldq $0x8,%xmm3,%xmm0
   0x000000000000109e <+94>:    vpxor  %xmm0,%xmm3,%xmm3
   0x00000000000010a2 <+98>:    vmovq  %xmm3,%rsi
   0x00000000000010a7 <+103>:   vzeroupper 
   0x00000000000010aa <+106>:   callq  0x1030 <printf@plt>
   0x00000000000010af <+111>:   xor    %eax,%eax
   0x00000000000010b1 <+113>:   leaveq 
   0x00000000000010b2 <+114>:   retq   
End of assembler dump.
В компиляторе gc (который знаком большинству людей) нет векторизации циклов. Вообще. By design. Никто пока не предложил как её внедрить, чтобы не нарушить одно из:
1. Не замедляет время компиляции.
2. Реализация не слишком сложная (maintainability).
3. Имеет примеры важного кода, который будет сильно ускоряться, кроме синтетики.

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

Математический код и HPC может и получит буст от векторизации, но для gc по-моему это не самые частые пользователи. Возможно в будущем что-то изменится, но пока ситуация такая. Где-то ещё были разные связанные с этим proposal'ы, в том числе о введении примитивов для использования FMA инструкций, но под рукой списка нет, можете поискать на github трекере, если интересно.

Возможно LLVM-based компилятор будет лучше, но по-моему там пока ещё не достаточно всё зрелое.
Завтра попробую intel компилятор, но по факту уже чистые sse2 инструкции дают хороший результат, хотя в большинстве прикладных задач, конечно этого не требуется

LLVM успешно развернет и завекторизует.
Т.е. даже C# с бэкендом LLVM через mono-llvm или Unity Burst будет быстрее
PS: вообще я удивлен почему компиляторы не посчитали цикл в компайл тайме и не заменили константой — возможно через какой-нибудь llvm-souper/polly можно оптимизнуть

хотя если вместо ^ использовать &,+,* — то посчитает в компайл-тайме

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

Скомпилировал ваш Си код clang с -O2 -march=native (или -mcpu=haswell) — он развернул ваш цикл, распихал все инты по всем доступным AVX регистрам.
https://godbolt.org/z/iLkRS5
ЗЫ: даже без avx все равно будет быстрее — будет тот же анроллинг и все SSE регистры


real 0m0.003s
user 0m0.001s
sys 0m0.001s

Код который отвечает за цикл при компиляциях gccgo vs gcc одинаков абсолютно. gccgo с -O3 тоже вкорячивает те же avx инструкции. Посмотрите godbolt.org/z/CvFV-X
Вы в итоге хотите сравнить циклы C vs Go а на деле сравниваете накладные расходы на запуск приложения, на гошный рантайм, на специфику работы printf.
Да и на самом деле смысл Go это больше про скорость разработки а не про тесты производительности с C.
Да я согласен, время тут сравнивать совсем не корректно, go запускает рабочие треды, шедулер, сборщик мусора, причем родной компилятор Go делает запуск быстрее. И скорее всего исходя из опыта использования google tcmalloc vs стандартный malloc, в много-тредовых сервисах go может показать сравнимые, а может и лучшие результаты относительно сишных сервисов основанных на обычном маллоке и posix тредах.
Все же удалось использовать SSE с float типом, видимо с int компилятор считает это преждевременной оптимизацией
Видимо в связи с тем, что штатный компилятор отлично оптимизирует запуск тредов (в моем случае на 4 ядра было создано 5 дополнительных тредов), приложение отрабатывает быстрее.

У меня одного эта фраза вызвала лёгкое недоумение?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории