Значительно ускоренно набирается python (возможно, используя Cython) - PullRequest
1 голос
/ 24 апреля 2020

У меня есть некоторый Python код с расширенными аннотациями типов, классы, которые по сути являются прославленными структурами (свойство ниже - единственный метод в моих классах) и необходимость его быстрого выполнения.

@dataclass
class Family:
    """A family group agent."""
    descendence: str
    culture: int
    location_history: List[h3.c_int] = field(default_factory=list)
    @property
    def location(self) -> h3.c_int:
        return self.location_history[0]
    number_offspring: int = 0
    effective_size: int = 2
    stored_resources: float = 130000.0
    seasons_till_next_child: int = 4
    seasons_till_next_mutation: Optional[int] = None

@dataclass
class Patch:
    """A patch of land with resources."""
    resources: float
    max_resources: float

Например, у меня есть agents: List[Tuple[Patch, Sequence[Family]]] (это немного сложнее, но это не имеет значения для вопроса о принципе) и эти две функции

def extract_resources(
        patch: Patch, group: Sequence[Family], total_labor_here: int) -> kcal:
    """Distribute the resources gained from cooperative extraction."""
    labor = sum([family.effective_size
                 for family in group])
    resources_extracted = resources_from_patch(
        patch, labor, total_labor_here - labor)
    for family in group:
        family.stored_resources += (
            resources_extracted * family.effective_size / labor)
    return resources_extracted

и

def resources_from_patch(
        patch: Patch,
        labor: int,
        others_labor: int,
        estimate: bool = False) -> float:
    """Compute or estimate the resources a cooperative gains from a patch."""
    my_relative_returns = (
        time_step_energy_use * labor *
        effective_labor_through_cooperation(labor))
    if not estimate:
        my_relative_returns = numpy.maximum(
            random.gauss(
                mu=my_relative_returns,
                sigma=(params.payoff_standarddeviation *
                       time_step_energy_use / labor ** 0.5)),
            0)
    if others_labor:
        others_relative_returns = (
            time_step_energy_use * others_labor *
            effective_labor_through_cooperation(others_labor))
        if not estimate:
            others_relative_returns = numpy.maximum(
                random.gauss(
                    mu=others_relative_returns,
                    sigma=(params.payoff_standarddeviation *
                           time_step_energy_use / others_labor ** 0.5)),
                0)
    else:
        others_relative_returns = 0
    return (my_relative_returns) / (
        my_relative_returns + others_relative_returns) * min(
            my_relative_returns + others_relative_returns,
            patch.resources * params.accessible_resources)

и я хочу выполнить

for patch, families in agents:
    extract_resources(patch, families, sum(family.effective_size for family in families))

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

Я бы хотел сохраните это как что-то, что может быть интерпретировано python, проверено mypy и python линтерами. Я предполагаю, что чистый python режим Cython мог бы помочь мне достичь этого, но я не понимаю, как изменить этот исходный код, чтобы он выполнялся максимально быстрым способом в Cython при этих ограничениях. Я посмотрел на numba, но я не нашел способа заставить его соблюдать мои структурированные классы, аннотированные типом.

Какие аннотации типов, декораторы и незначительные рефакторинги я должен использовать для выполнить этот стиль кода Python самым быстрым параллельным способом без ограничения GIL?

...