Я нахожу, что я получаю доступ к Python-диктам с использованием статических строк очень часто и во вложенных циклах. Я предполагаю, что Python должен хэшировать строки для каждого доступа, что может повлиять на производительность.
Например:
for d in large_list_of_dicts:
for d2 in d['trees']:
v = d2['leaves']
Хитует ли Cython эти статические строковые ключи для хэш-значений (только один раз) и повторно использует эти хэши? Если да, может ли это значительно улучшить производительность таких циклов?
Cython не будет выполнять здесь никакой магии: он просто делегирует вызовы PyDict_GetItemWithError
- в основном то же самое, что будет делать интерпретатор Python (но, вероятно, немного быстрее).
Однако unicode-объект (я предполагаю, что мы говорим о Python3-строках) кэширует свое хеш-значение (в PyUnicodeObject.hash
-member), поэтому его необходимо вычислять только один раз - что имеет смысл, потому что Unicode-объект является неизменным, это означает, что хэш не может измениться.
Вот код CPython, ответственный за вычисление/кэширование хеша:
#define _PyUnicode_HASH(op) \
(((PyASCIIObject *)(op))->hash)
...
static Py_hash_t
unicode_hash(PyObject *self)
{
...
// if hash already calculated, return cached value
if (_PyUnicode_HASH(self) != -1)
return _PyUnicode_HASH(self);
...
// else caclculate hash, cache value, return it
x = _Py_HashBytes(PyUnicode_DATA(self),
PyUnicode_GET_LENGTH(self) * PyUnicode_KIND(self));
_PyUnicode_HASH(self) = x;
return x;
}
Итак, как вы можете видеть, Cython не нужно избегать пересчета хеша - эта оптимизация уже выполнена CPython.
Используя здесь Cython, можно выиграть до 10-30%, потому что это исключит интерпретатор для этой части кода (см., Например, этот SO-пост) - не очень много, но лучше, чем ничего.