История изменений
Исправление firkax, (текущая версия) :
Если у тебя цикл и компилятор его векторизовал, то получается мягко говоря не понятный и не закономерный асм. Да что там векторизация — ты пробовал обычные циклы почитать? Оно весь поток управления перетряхивает уже на -O1.
Векторизованные вроде не видел (наверно это связано с тем что на всех десктопах у меня 32 бита), а насчёт потока управления - не уверен что понял о чём речь. Разворачивание циклов, индивидуальные реализации первой/последней итерации, оптимизация переменной цикла? Если ты напишешь что-то типа
for(i=0; i<j; i++) {
x = 0;
f(a[k-i], b[m-i]);
}
то оно может превратиться в что-то типа
x = 0;
for(i=a+k; i>a+k-j; i--) {
f(*i,*(b+m-k+(i-a)));
}
ну норм, читабельности это не вредит. К тому же я часто сам второй вариант сразу и пишу. Или ты про другое?
А у меня сложилось впечатление, что если программист вместо логики занимается ручной оптимизацией в малозначимых функциях, то он занимается бесполезной херней.
Значимость функций это понятие относительное. Ну да, не везде надо оптимизировать. Где не надо - там незачем и что-то кроме -O0 использовать, и можно писать на пхп вообще. Ну и в целом если тебя интересует только логика, то наверно Си не для тебя, да, есть другие языки где постарались уберечь программиста от низкоуровневых артефактов.
Я вел речь про volatile и подобные по роли функции, вроде встроенных атомиков или asm volatile.
Есть конкретные примеры? Я кажется понял о чём речь, но вроде такого не должно происходить без указанных мной опций. strict-aliasing, повторюсь, дефолт для -O2 поэтому велик шанс что оно было включено там где ты с этим столкнулся.
s1 = s + «asd» можно сделать в Си? Отличные истории.
Да, можно, если s - число, а s1 - указатель. А если серьёзно, то я уже писал - сишная поддержка строк состоит из синтаксиса строковых литералов, и только из него. Конкатенация строк реализуется уже кодом. Перегрузить оператор плюс в Си нельзя, автоматически следить за тем, когда надо освободить память, занятую аллоцированными строками - тоже. Поэтому конкатенация в любом случае будет, во-первых, функцией, а во-вторых, скорее всего, придётся вручную расставлять подсказки для освобождения памяти, занятой аргументами. Если это всё - проблема, то есть С++, где есть и перегрузка плюса и автоматическое освобождение памяти когда надо, но это уже другая тема. А Си рассчитан именно на максимально явное задание алгоритма во всех аспектах, и в некоторых случаях это приводит к очень громоздкому коду, да.
конкатенирование нуль-терминированных строк без лежащей рядом длины не масштабируется.
Ну да, но Си тут почти ни при чём. Повторюсь, можно писать на Си и использовать строки с хранимой длиной. И даже более-менее спрятать детали реализации за функции и макросы, но да, на С++ всё равно формулы подобных операций будут красивее.
Что касается твоего прошлого заявления что вот в языках, где строки нативно и безальтернативно хранятся как { size_t length; char data[]; }, конкатенация быстрее - это не совсем так. Да, в некоторых случаях это будет оптимально, может быть даже в многих из возникающих на практике, но не забывай, что как минимум один из аргументов (а скорее всего - оба, если исходные значения надо сохранить, либо если предыдущему коду не повезло угадать с выделением памяти про запас) придётся копировать в другое место. Это быстрее чем вычисление длины, но все же сравнимо по времени (представь строки по гигабайту каждая). А вот в Си (или С++) ты можешь написать например такую реализацию строк, которая копировать ничего не будет (а просто запомнить, что первая половина новой строки лежит там-то, а вторая - там-то, разумеется с подсчётом рефов и cow). Либо не первая-вторая в например сотня сегментов, каждый из которых можно cow по отдельности. Либо ещё что-нить. В этом и преимущество Си - ты можешь низкоуровневый алгоритм тонко настроить под нужды одного конкретного места в коде, где ты им пользуешься. Более того, ты можешь не просто тюнить этот алгоритм как отдельную сущность, а заинлайнить его в вызывающий код и оптимизировать дальше уже то, что получилось. В языках, прячущих низкий уровень от программиста, всё это получится только костылями, а большинство просто не станут париться и задействуют неэффективную штатную реализацию. Но да, в среднестатистическом применении у них бывают удобные наборы готовых абстракций.
Исправление firkax, :
Если у тебя цикл и компилятор его векторизовал, то получается мягко говоря не понятный и не закономерный асм. Да что там векторизация — ты пробовал обычные циклы почитать? Оно весь поток управления перетряхивает уже на -O1.
Векторизованные вроде не видел (наверно это связано с тем что на всех десктопах у меня 32 бита), а насчёт потока управления - не уверен что понял о чём речь. Разворачивание циклов, индивидуальные реализации первой/последней итерации, оптимизация переменной цикла? Если ты напишешь что-то типа
for(i=0; i<j; i++) {
x = 0;
f(a[k-i], b[m-i]);
}
то оно может превратиться в что-то типа
x = 0;
for(i=a+k; i>a+k-j; i--) {
f(*i,*(b+m-k+(i-a)));
}
ну норм, читабельности это не вредит. К тому же я часто сам второй вариант сразу и пишу. Или ты про другое?
А у меня сложилось впечатление, что если программист вместо логики занимается ручной оптимизацией в малозначимых функциях, то он занимается бесполезной херней.
Значимость функций это понятие относительное. Ну да, не везде надо оптимизировать. Где не надо - там незачем и что-то кроме -O0 использовать, и можно писать на пхп вообще. Ну и в целом если тебя интересует только логика, то наверно Си не для тебя, да, есть другие языки где постарались уберечь программиста от низкоуровневых артефактов.
Я вел речь про volatile и подобные по роли функции, вроде встроенных атомиков или asm volatile.
Есть конкретные примеры? Я кажется понял о чём речь, но вроде такого не должно происходить без указанных мной опций. strict-aliasing, повторюсь, дефолт для -O2 поэтому велик шанс что оно было включено там где ты с этим столкнулся.
s1 = s + «asd» можно сделать в Си? Отличные истории.
Да, можно, если s - число, а s1 - указатель. А если серьёзно, то я уже писал - сишная поддержка строк состоит из синтаксиса строковых литералов, и только из него. Конкатенация строк реализуется уже кодом. Перегрузить оператор плюс в Си нельзя, автоматически следить за тем, когда надо освободить память, занятую аллоцированными строками - тоже. Поэтому конкатенация в любом случае будет, во-первых, функцией, а во-вторых, скорее всего, придётся вручную расставлять подсказки для освобождения памяти, занятой аргументами. Если это всё - проблема, то есть С++, где есть и перегрузка плюса и автоматическое освобождение памяти когда надо, но это уже другая тема. А Си рассчитан именно на максимально явное задание алгоритма во всех аспектах, и в некоторых случаях это приводит к очень громоздкому коду, да.
конкатенирование нуль-терминированных строк без лежащей рядом длины не масштабируется.
Ну да, но Си тут почти ни при чём. Повторюсь, можно писать на Си и использовать строки с хранимой длиной. И даже более-менее спрятать детали реализации за функции и макросы, но да, на С++ всё равно формулы подобных операций будут красивее.
Что касается твоего прошлого заявления что вот в языках, где строки нативно и безальтернативно хранятся как { size_t length; char data[]; }, конкатенация быстрее - это не совсем так. Да, в некоторых случаях это будет оптимально, может быть даже в многих из возникающих на практике, но не забывай, что как минимум один из аргументов (а скорее всего - оба, если исходные значения надо сохранить, либо если предыдущему коду не повезло угадать с выделением памяти про запас) придётся копировать в другое место. Это быстрее чем вычисление длины, но все же сравнимо по времени (представь строки по гигабайту каждая). А вот в Си (или С++) ты можешь написать например такую реализацию строк, которая копировать ничего не будет (а просто запомнить, что первая половина новой строки лежит там-то, а вторая - там-то, разумеется с подсчётов рефов и cow). Либо не первая-вторая в например сотня сегментов, каждый из которых можно cow по отдельности. Либо ещё что-нить. В этом и преимущество Си - ты можешь низкоуровневый алгоритм тонко настроить под нужды одного конкретного места в коде, где ты им пользуешься. Более того, ты можешь не просто тюнить этот алгоритм как отдельную сущность, а заинлайнить его а вызывающий код и оптимизировать дальше уже то, что получилось. В языках, прячущих низкий уровень от программиста, всё это получится только костылями, а большинство просто не станут париться и задействуют неэффективную штатную реализацию. Но да, в среднестатистическом применении у них бывают удобные наборы готовых абстракций.
Исходная версия firkax, :
Если у тебя цикл и компилятор его векторизовал, то получается мягко говоря не понятный и не закономерный асм. Да что там векторизация — ты пробовал обычные циклы почитать? Оно весь поток управления перетряхивает уже на -O1.
Векторизованные вроде не видел (наверно это связано с тем что на всех десктопах у меня 32 бита), а насчёт потока управления - не уверен что понял о чём речь. Разворачивание циклов, индивидуальные реализации первой/последней итерации, оптимизация переменной цикла? Если ты напишешь что-то типа
for(i=0; i<j; i++) {
x = 0;
f(a[k-i], b[m-i]);
}
то оно может превратиться в что-то типа
x = 0;
for(i=k; i>k-j; i--) {
f(a[i],b[m-k+i]);
}
ну норм, читабельности это не вредит. К тому же я часто сам второй вариант сразу и пишу. Или ты про другое?
А у меня сложилось впечатление, что если программист вместо логики занимается ручной оптимизацией в малозначимых функциях, то он занимается бесполезной херней.
Значимость функций это понятие относительное. Ну да, не везде надо оптимизировать. Где не надо - там незачем и что-то кроме -O0 использовать, и можно писать на пхп вообще. Ну и в целом если тебя интересует только логика, то наверно Си не для тебя, да, есть другие языки где постарались уберечь программиста от низкоуровневых артефактов.
Я вел речь про volatile и подобные по роли функции, вроде встроенных атомиков или asm volatile.
Есть конкретные примеры? Я кажется понял о чём речь, но вроде такого не должно происходить без указанных мной опций. strict-aliasing, повторюсь, дефолт для -O2 поэтому велик шанс что оно было включено там где ты с этим столкнулся.
s1 = s + «asd» можно сделать в Си? Отличные истории.
Да, можно, если s - число, а s1 - указатель. А если серьёзно, то я уже писал - сишная поддержка строк состоит из синтаксиса строковых литералов, и только из него. Конкатенация строк реализуется уже кодом. Перегрузить оператор плюс в Си нельзя, автоматически следить за тем, когда надо освободить память, занятую аллоцированными строками - тоже. Поэтому конкатенация в любом случае будет, во-первых, функцией, а во-вторых, скорее всего, придётся вручную расставлять подсказки для освобождения памяти, занятой аргументами. Если это всё - проблема, то есть С++, где есть и перегрузка плюса и автоматическое освобождение памяти когда надо, но это уже другая тема. А Си рассчитан именно на максимально явное задание алгоритма во всех аспектах, и в некоторых случаях это приводит к очень громоздкому коду, да.
конкатенирование нуль-терминированных строк без лежащей рядом длины не масштабируется.
Ну да, но Си тут почти ни при чём. Повторюсь, можно писать на Си и использовать строки с хранимой длиной. И даже более-менее спрятать детали реализации за функции и макросы, но да, на С++ всё равно формулы подобных операций будут красивее.
Что касается твоего прошлого заявления что вот в языках, где строки нативно и безальтернативно хранятся как { size_t length; char data[]; }, конкатенация быстрее - это не совсем так. Да, в некоторых случаях это будет оптимально, может быть даже в многих из возникающих на практике, но не забывай, что как минимум один из аргументов (а скорее всего - оба, если исходные значения надо сохранить, либо если предыдущему коду не повезло угадать с выделением памяти про запас) придётся копировать в другое место. Это быстрее чем вычисление длины, но все же сравнимо по времени (представь строки по гигабайту каждая). А вот в Си (или С++) ты можешь написать например такую реализацию строк, которая копировать ничего не будет (а просто запомнить, что первая половина новой строки лежит там-то, а вторая - там-то, разумеется с подсчётов рефов и cow). Либо не первая-вторая в например сотня сегментов, каждый из которых можно cow по отдельности. Либо ещё что-нить. В этом и преимущество Си - ты можешь низкоуровневый алгоритм тонко настроить под нужды одного конкретного места в коде, где ты им пользуешься. Более того, ты можешь не просто тюнить этот алгоритм как отдельную сущность, а заинлайнить его а вызывающий код и оптимизировать дальше уже то, что получилось. В языках, прячущих низкий уровень от программиста, всё это получится только костылями, а большинство просто не станут париться и задействуют неэффективную штатную реализацию. Но да, в среднестатистическом применении у них бывают удобные наборы готовых абстракций.