Соглашение о вызове Windows x86-64 с только 6 списками xmm с записью с записью не очень хорошее, вы правы. Большинство SIMD (и многих скалярных FP) циклов не содержат каких-либо вызовов функций, поэтому они ничего не получают от того, что их данные хранятся в сохраняемых вызовах. Сохранение / восстановление - это чистый недостаток, потому что это редкость, чем любой из их вызывающих пользователей использует это энергонезависимое состояние.
В x86-64 System V все векторные регистры сбрасываются по вызову, что может быть, слишком далеко. Во многих случаях было бы хорошо сохраниться 1 или 2 вызова, особенно для кода, который вызывает некоторые вызовы функций библиотеки. ( Используйте gcc -fno-math-errno
, чтобы упростить простые строки , иногда единственная причина, по которой они не делают, - это установить errno
на NaN.)
Связано: , как было выбрано соглашение о вызове x86-64 SysV : просмотр размера кода и подсчета команд для gcc-компиляции SPECint / SPECfp.
Для целых регистров, имеющих некоторые из них, определенно хорошо, и все «обычные» соглашения о вызовах (для всех архитектур, а не только x86) действительно имеют микс. Это уменьшает общий объем проделанной работы, выполняемой при размножении / восстановлении в вызываемых и вызываемых абонентах.
Заставляя вызывающего абонента разливать / перезагружать все вокруг каждого вызова функции не подходит для размера кода или производительности. Сохранение / восстановление некоторых сохраняемых вызовов regs в начале / конце функции позволяет не-листовым функциям сохранять некоторые вещи в регистре через call
s.
Рассмотрим некоторый код, который вычисляет пару вещей, а затем cout << "result: " << a << "foo" << b*c << '\n';
Это 4 вызова функций std::ostream operator<<
, и они обычно не встроены. Сохранение адреса cout
и местных жителей, которые вы просто вычислили в энергонезависимых регистрах, означает, что вам нужны только некоторые дешевые mov reg,reg
инструкции для настройки аргументов для следующего вызова. (Или push
в соглашении о вызове stack-args).
Но наличие некоторых регистров, сбрасываемых вызовом, которые могут использоваться без сохранения, также очень важно. Функции, которым не нужны все архитектурные регистры, могут просто использовать регистры с затухающими вызовами как временные. Это позволяет избежать разлива / перезагрузки в критический путь для цепочек зависимостей вызывающего абонента (для очень маленьких абонентов), а также для сохранения инструкций.
Иногда сложная функция будет сохранять / восстанавливать некоторые регистры с сохранением вызовов только чтобы получить больше полных регистров (например, вы видите XMM для хрустания числа). Это вообще стоит того; сохранение / восстановление энергонезависимых регистров вызывающего абонента обычно лучше, чем проливание / перезагрузка собственных локальных переменных в стек, особенно если вы не должны делать это внутри любого цикла.
Еще одна причина для регистры с затухающими звонками - это то, что обычно некоторые из ваших значений «мертвы» после вызова функции: вам нужны только их как аргументы функции. Вычисление их в списках, записанных с помощью call-clobbered, означает, что вам не нужно сохранять / восстанавливать что-либо, чтобы освободить эти регистры, но также и то, что ваш собеседник также может свободно их использовать. Это еще лучше при вызове соглашений, которые передают args в регистры: вы можете вычислять свои входы непосредственно в регистры, проходящие через arg. (И скопируйте все в защищенные вызовом регистры или проливайте их на стеке памяти, если они вам также понадобятся после этой функции.)
(Мне нравятся термины, сохраненные по вызову, по сравнению с call-clobbered, спасенный или спасенный. Последние термины подразумевают, что кто-то должен сохранять регистры вместо того, чтобы просто позволить мертвым значениям умереть. volatile / non-volatile не плохо, но эти термины также имеют другие технические значения как ключевые слова C или в терминах вспышки против DRAM.)
You can use a CASE
statement to tweak ordering:
SELECT *
FROM table
ORDER BY case when no is null then 2 else 1 end, no
This orders on "nullableness" first, and no
second.
Ok, I think I got it:
SELECT * FROM table WHERE no IS NOT NULL ORDER BY no ASC
UNION
SELECT * FROM table WHERE no IS NULL
Or is there any better way ?
SELECT * FROM table ORDER BY COALESCE(no,999999) ASC
Just replace the 999999 with something larger if your numbers are naturally bigger than that.