44 минуты про сравнения, что может быть лучше?
https://www.youtube.com/watch?v=o-Ng_73kdik
В видео будет про:
- Сравнения в Python2 и усиление типизации в Python3
- Оптимизация байткода в Tier1:
COMPARE_OP превращается в COMPARE_OP_{INT,STR,FLOAT}- Разницу байткода и перформанса между
a == b == c и a == b and b == c-
PyObject_RichCompare C-API- Работу с
NotImplemented- Дефолтную реализацию
object.__eq__, object.__lt__ и другихИ даже за 44 минуты я не успел рассказать все! Делюсь дополнительными материалами здесь.
1. В ролике был вопрос: почему перед опкодом
TO_BOOL идет дополнительный COPY? Ответ будет такой. Как выглядит опредление опкода TO_BOOL? op(_TO_BOOL, (value -- res)). Теперь, давайте разбираться, что такое (value -- res). bytecodes.c написан на специальном DSL, который упрощает определение работы с байткодом для разных уровней оптимизаторов, а так же делает работу со стеком виртуальной машины похожей на "вызов функций". (value -- res) значит: возьми со стека value, положи на стек res
op(_TO_BOOL, (value -- res)) {
int err = PyObject_IsTrue(PyStackRef_AsPyObjectBorrow(value));
DECREF_INPUTS();
ERROR_IF(err < 0, error);
res = err ? PyStackRef_True : PyStackRef_False;
}
Следовательно, если у нас не будет
COPY:
pure inst(COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) {
assert(oparg > 0);
top = PyStackRef_DUP(bottom);
}
То, мы для превращения объекта в
bool в TO_BOOL возьмем со стека value, превратим его в bool, сложим на стек результат, его проверит POP_JUMP_IF_FALSE. А самого значения уже не останется. COPY позволяет сохранить само значение объекта в стеке, для дальнейшей работы с ним.Документация: https://github.com/python/cpython/blob/main/Tools/cases_generator/interpreter_definition.md
2. Я не показал
long_compare. Исправляюсь:
static Py_ssize_t
long_compare(PyLongObject *a, PyLongObject *b)
{
if (_PyLong_BothAreCompact(a, b)) {
return _PyLong_CompactValue(a) - _PyLong_CompactValue(b);
}
Py_ssize_t sign = _PyLong_SignedDigitCount(a) - _PyLong_SignedDigitCount(b);
if (sign == 0) {
Py_ssize_t i = _PyLong_DigitCount(a);
sdigit diff = 0;
while (--i >= 0) {
diff = (sdigit) a->long_value.ob_digit[i] - (sdigit) b->long_value.ob_digit[i];
if (diff) {
break;
}
}
sign = _PyLong_IsNegative(a) ? -diff : diff;
}
return sign;
}
Исходник: https://github.com/python/cpython/blob/656b7a3c83c79f99beac950b59c47575562ea729/Objects/longobject.c#L3548-L3567
Код кажется очевидным. Теперь полный цикл сравнений пройден :)
Вопрос для обсуждения:
a == b == c или a == b and b == c? Какой стиль вы выбираете у себя?Поддержать такой контент можно тут:
- https://boosty.to/sobolevn
- https://github.com/sponsors/wemake-services