Как сделать так, чтобы расчет среднего балла не превышал 0,01 секунды?

Я создал функцию для усреднения комментариев в TVAndMovie. Однако, когда я создал 100000 комментариев, для получения среднего значения оценок потребовалось в среднем 4 секунды! Я пытался найти способ получить среднее значение оценок. Как я могу сократить время до менее чем 0,01 секунды?

class TVAndMovie(models.Model):
    tmdb_id = models.IntegerField(
        verbose_name="",
        blank=False,
        null=False,
    )
    
    judge_tv_or_movie = models.CharField(
        blank=False, null=False, default="movie", max_length=20
    )
    stars = models.FloatField(
        blank=False,
        null=False,
        default=0,
        validators=[MinValueValidator(0.0), MaxValueValidator(10.0)],
    )

    def get_comments(self) -> object:
        return Comment.objects.filter(
            tv_or_movie_id=self.id
        )

    def average_stars(self) -> float:
        comments = self.get_comments()
        n_comments = comments.count()
        if n_comments:
            self.stars = round(
                sum([comment.stars for comment in comments]) / n_comments, 3
            )
        else:
            self.stars = 0
        self.save()
        return self.stars

class Comment(models.Model):

    comment = models.TextField(max_length=1000)
    stars = models.FloatField(
        blank=False,
        null=False,
        default=0,
        validators=[MinValueValidator(0.0), MaxValueValidator(10.0)],
    )
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    tv_or_movie = models.ForeignKey(TVAndMovie, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ("user", "tv_or_movie")
        indexes = [models.Index(fields=["user", "tv_or_movie"])]

Поэтому я решил отладить ваш код и измерить производительность - поскольку я использую более быстрый компьютер (для 100k записей он выполняет метод average_stars менее чем за несколько секунд), я создал 500k Comments. Тогда этот метод выполняется примерно за 4,5с.

Я измерил производительность с помощью отладочного представления:

class IndexView(TemplateView):

    def get(self, *args, **kwargs):
        tv_or_movie = TVAndMovie.objects.first()
        start = time.time()
        stars = tv_or_movie.average_stars()
        end = time.time()
        return JsonResponse({
            'finish': end-start,
            'rate': stars,
        })

Затем я попытался написать оптимизированный код - вместо вызова прямого запроса я решил использовать директиву _set и затем привести stars значения к list с помощью метода values_list.

Таким образом я получил 0.2sвремя выполнения (производительность). Код:

    def average_stars(self) -> float:
        get_stars = list(self.comment_set.values_list('stars', flat=True))
        if get_stars:
            self.stars = round(
                sum(get_stars) / len(get_stars), 3
            )
        self.save()
        return self.stars

Обычно использование .count() неплохо, но len() быстрее, и вы можете выполнить его на list, которые вы получили при кэшировании stars значений в сказанное list.

Также я думаю, что это самый быстрый и простой способ вычисления среднего рейтинга, вы, вероятно, не получите меньше, чем 0.01s производительность, но я думаю, что этого достаточно для вас.

Если у вас есть вопросы, ответьте мне в комментариях.

Вернуться на верх