Как сделать так, чтобы расчет среднего балла не превышал 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 производительность, но я думаю, что этого достаточно для вас.
Если у вас есть вопросы, ответьте мне в комментариях.