【问题标题】:For loop and too much loops is making the execution time very longer?For循环和太多的循环使执行时间变长了?
【发布时间】:2018-01-05 05:45:36
【问题描述】:

我目前正面临一场噩梦。我的数据库几乎只有 400 条记录,问题是我的 PHP 代码正在生成图像滑块,因此代码几乎需要 60 秒 才能执行 >400条记录,时间太长了,没有人能忍受等待这么长时间。 我真的很担心,因为我不确定可以做些什么来让它更快,但据我所知,使用 foreach 循环而不是 for 循环可以让事情变得更快,但我想知道你们是否可以取悦为我提供解决方案。

它是 Symfony 站点内的部分模板,它呈现图像滑块并从数据库中获取每个图像的滑块。但我并不担心 SQL 查询,因为执行所有查询以获取这些滑块图像最多只需要 3 到 4 秒,但是生成滑块的这段代码需要很多时间在大多数情况下,更多的时间几乎是 60 多秒,这对于任何人来说都需要等待太多时间。

如果你们可以为我提供优化的代码以获得最佳性能,我将不胜感激。

这里的统计数据如下:

这是我的代码 (_searchResults.php)

<?php if (count($locations) > 0): ?>
  <?php foreach ($locations as $location): ?>
    <?php if ($location->PhotoFiles->count()): ?>
      <li class="item" data-location-id="<?php echo $location->id ?>">
        <div class="searchItemSlider">
          <?php $limit = ($location->PhotoFiles->count() >= 10 ? 10 : $location->PhotoFiles->count()); ?>
          <?php for ($i = 1; $i < ($limit + 1); $i++): ?>
            <?php $photo = $location->findPhotoIndex($i); ?>
            <?php //if (1): ?>
            <?php  if ($photo && $thumb = $photo->getThumbnailByType(ThumbnailTable::THUMB_520_392)): ?>
              <div class="searchItem">
                <figure>
                  <?php  if (file_exists($thumb->getFullFilesystemPath())): ?>
                    <?php //if (1 == 2): ?>
                    <div class="thumb-img">
                      <img class="lazyOwl" src="<?php echo $thumb->getFullWebPath() ?>" />
                    </div>
                  <?php else: ?>
                    <div class="thumb-img">
                      <img class="lazyOwl" src="/images/newfrontend/categories/no_venue.jpg" />
                    </div>
                  <?php endif; ?>
                  <figcaption>
                    <a href="<?= url_for("@venue_details?id=" . $location->id) ?>" title="<?php echo $location->venue_name; ?>"><?php echo $location->venue_name; ?></a>
                    <br />
                  </figcaption>
                  <?php $districtName = (strlen($location->venue_area)) ? $location->venue_area : $location->getGeoLocationDistrictName();
                    if (strlen($districtName)): ?>
                    <div class="district_name"><a href="<?= url_for("@venue_details?id=" . $location->id) ?>" title="<?php echo $location->venue_name; ?>"><?php echo $districtName ?></a></div>
                  <?php endif; ?>
                </figure>
                <p><?php echo substr(strip_tags(html_entity_decode($location->description)), 0, 100) ?></p>
                <span class="eye"><a href="<?= url_for("@venue_details?id=" . $location->id) ?>" title="<?php echo $location->venue_name; ?>"><img src="/images/newfrontend/icons/eye-icon.png"/></a></span>

                <div class="links">                  
                  <a href="<?php echo url_for('@venue_share?id=' . $location->id) ?>" class="arrow share_links" data-toggle="tooltip" title="Share this venue">&nbsp;</a>
                  <?php if (!$sf_user->locationInWishlist($location->id)): ?>
                    <a href="<?php echo url_for('@wishlist_addtowishlistajaxraw') ?>" data-attr-venue-id="<?php echo $location->id ?>" class="pin add_to_wishlist" data-toggle="tooltip" title="Add to wishlist">&nbsp;</a>
                  <?php else: ?>
                    <a href="javascript:void(0)" class="added-to-wishlist">&nbsp;</a>
          <?php endif; ?>
                </div>
              </div>
            <?php endif; ?>
      <?php endfor; ?>
        </div>
      </li>
    <?php else: ?>
      <li class="item" data-location-id="<?php echo $location->id ?>">
        <div class="searchItemSlider">
          <div class="searchItem">
            <figure>
              <div class="thumb-img">
                <img class="lazyOwl" src="/images/newfrontend/categories/no_venue.jpg" />
              </div>
              <figcaption>
                <a href="<?= url_for("@venue_details?id=" . $location->id) ?>" title="<?php echo $location->venue_name; ?>"><?php echo $location->venue_name; ?></a>
                <br />
              </figcaption>
              <?php $districtName = (strlen($location->venue_area)) ? $location->venue_area : $location->getGeoLocationDistrictName();

              if (strlen($districtName)):
                ?>
              <div class="district_name"><a href="<?= url_for("@venue_details?id=" . $location->id) ?>" title="<?php echo $location->venue_name; ?>"><?php echo $districtName ?></a></div>
              <?php endif; ?>
            </figure>
            <p><?php echo substr(strip_tags(html_entity_decode($location->description)), 0, 100) ?></p>
            <span class="eye"><a href="<?= url_for("@venue_details?id=" . $location->id) ?>" title="<?php echo $location->venue_name; ?>"><img src="/images/newfrontend/icons/eye-icon.png"/></a></span>

            <div class="links">                  
              <a href="<?php echo url_for('@venue_share?id=' . $location->id) ?>" class="arrow share_links" title="Share this venue">&nbsp;</a>
              <?php if (!$sf_user->locationInWishlist($location->id)): ?>
                <a href="<?php echo url_for('@wishlist_addtowishlistajaxraw') ?>" data-attr-venue-id="<?php echo $location->id ?>" class="pin add_to_wishlist" data-toggle="tooltip" title="Add to wishlist">&nbsp;</a>
              <?php else: ?>
                <a href="javascript:void(0)" class="added-to-wishlist">&nbsp;</a>
              <?php endif; ?>
            </div>
          </div>
        </div>
      </li>
    <?php endif; ?>
  <?php endforeach; ?>
<?php endif; ?>

更新:

位置: /lib/model/doctrine/PhotoFile.class.php

  /**
   * Returns the first thumbnail of a certain type
   * or false if no thumbnail matches
   *
   * @param const $type The type of thumbnail, from the ThumbnailTable::THUMB_* constants
   * @return bool|Thumbnail
   */
  public function getThumbnailByType($type) {
    foreach ($this->Thumbnails as $t) {
      if ($t->thumb_type == $type) {
        return $t;
      }
    }
    return false;
  }

位置: /lib/model/doctrine/Location.class.php

   /**
   * Geocoder: get district name (administrative county)
   *
   * @return string
   */
  public function getDistrictName()
  {
    return ($this->hasGeocode() ? $this->getGeocode()->district_name : null);
  }

  public function getGeoLocationDistrictName()
  {
    $districtName = '';
    if($this->getGeocoded()->count() > 0)
    {
      if($this->getGeocoded()->getFirst()->getGeolocationPostcode()->count() > 0)
      {
        if($this->getGeocoded()->getFirst()->getGeolocationPostcode()->getFirst()->getGeolocationDistrict()->count() > 0)
        {
          $districtName = $this->getGeocoded()->getFirst()->getGeolocationPostcode()->getFirst()->getGeolocationDistrict()->getFirst()->getName();
        }
      }
    }
    return $districtName;
  }

 /**
   * Gets the geocode data for this location.
   * We store it locally so we don't need to keep querying for it.
   *
   * @return boolean|PcawCodes $pcawCode
   */
  public function getGeocode()
  {
    if ($this->Geocoded->count())
    {
      return $this->Geocoded->getFirst();
    }

    if (!strlen($this->address_postcode))
    {
      // No postcode to geocode
      return false;
    }

    $geocode = new JointGeocoder();
    try
    {
      /* @var $result PcawCodes */
      $result = $geocode->geocode("", $this->address_postcode);

      // Add the postcode also to the GeolocationPostcode table
      if($result) Doctrine::getTable('GeolocationPostcode')->addPostcodeIfMissing($result);
    }
    catch (Exception $e)
    {
      // Some problem occurred geocoding, return false
      return false;
    }

    return $result;
  }

【问题讨论】:

  • 你怎么知道,你有没有对你的代码进行基准测试,看看它在哪里花费时间?
  • only takes about 3 to 4 seconds at most to execute all queries 人的平均注意力持续时间约为 6 秒,我尝试在此范围内加载页面。
  • 你也可以避免重复计算,比如$location-&gt;PhotoFiles-&gt;count()被使用了3次,你可以将它存储在一个变量中并使用它
  • 你可以从我正在编写的Evolution 框架中借用我的基准测试类(是的,我正在编写自己的框架,无耻的插件)这里github.com/ArtisticPhoenix/Evo/blob/master/Evo/Benchmark.php
  • 你像这样使用它,$mark = Benchmark::getInstance()-&gt;mark('A'); ..other code ... echo Benchmark::getInstance()-&gt;format($mark); 它会说从开始到结束需要多长时间。

标签: php performance symfony processing-efficiency


【解决方案1】:

我不会具体告诉你如何解决这个问题,而是提出一些改进性能的建议。

  1. 3 - 4 秒完成查询是很长的时间。优化您的数据库架构、分析查询、使用适当的索引。

  2. 只查询你需要的数据。如果您不需要 400 个结果,请适当限制您的查询。

  3. 这些数据真正变化的频率如何?它总是需要最新的吗?将数据解析为控制器或服务类中所需的结构,然后将其缓存到文件或类似文件中。

  4. 读取缓存数据,而不是每次都打数据库。

  5. 不要即时或在需要时生成缩略图。上传时生成,或生成一次并缓存以备将来使用。

  6. 构建您的视图并缓存您的视图,这样您就不会每次都访问数据库。

  7. 使用适当的缓存标头提供您的内容,以便用户的浏览器缓存页面和/或资产。

【讨论】:

    【解决方案2】:

    我注意到,您使用的基准测试工具似乎表明该页面正在进行 4540 次查询。如果是这样,那可能就是原因。

    根据您的代码,我怀疑这是 n+1 问题 - 本质上是通过单独获取新模型在循环内进行另一个查询。大多数 ORM 都有一种通过预先加载来解决这个问题的机制,即它们预先获取所有这些行。使用急切加载还可以更轻松地缓存响应。

    我对 Doctrine 不熟悉,但根据https://tideways.io/profiler/blog/5-doctrine-orm-performance-traps-you-should-avoid 它支持急切加载。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-06
      • 1970-01-01
      • 2016-01-01
      • 2014-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-18
      相关资源
      最近更新 更多