首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >fputs缓慢地写入磁盘

fputs缓慢地写入磁盘
EN

Stack Overflow用户
提问于 2021-09-10 12:09:29
回答 1查看 37关注 0票数 1

我有一个将csv文件写入磁盘的php脚本,这是函数:

代码语言:javascript
复制
function fputcsv_content($fp, $array, $delimiter=",", $eol="\n") {

    $line = "";
    foreach($array as $value) {
        $value = trim($value);
        $value = str_replace("\r\n", "\n", $value);
        if(preg_match("/[$delimiter\"\n\r]/", $value)) {
            $value = '"'.str_replace('"', '""', $value).'"';
        }
        $line .= $value.$delimiter;
    }
    $eol = str_replace("\\r", "\r", $eol);
    $eol = str_replace("\\n", "\n", $eol);
    $line = substr($line, 0, (strlen($delimiter) * -1));
    $line .= $eol;
    return fputs($fp, $line);
}

服务器为AWS实例,PHP7,CentOS版本为7.2

服务器规格:4 4GB 32 4GB交换2核,2.5 4GB

当文件很大(3 3GB、4 3GB)时,写入过程非常慢(每2或3秒1MB)。

在php.ini或apache config中是否有任何设置来控制此fput/fwrite函数?

我在php.ini中看到了一个output_buffer设置(当前设置为4096),但我怀疑它与此无关。

谢谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-09-10 14:14:24

不要使用.=来追加一行。使用数组,将值添加到数组中,然后内爆该数组。你现在正在用不断丢弃的字符串填充你的内存。每次执行.=操作时,旧的字符串都会保留在堆栈中,新的空间会保留给新的字符串,而GC仅在函数准备就绪时运行。对于3-4 4gb的文件,最终可能是它的许多倍,这导致进程使用交换作为额外的内存,这是很慢的。

尝试将其重构为数组方法,并使用一些节省内存的技术,看看这是否会减轻您的问题。

我添加了静态函数变量的使用,这样它们只被赋值一次,而不是每次迭代,这也节省了少量的内存,把php可能做的或不做的任何优化都放在一边。

在线查看:https://ideone.com/dNkxIE

代码语言:javascript
复制
function fputcsv_content($fp, $array, $delimiter=",", $eol="\n") 
{
    static $find = ["\\r","\\n"];
    static $replace = ["\r","\n"];
    static $cycles_count = 0;
    $cycles_count++;
    
    $array = array_map(function($value) use($delimiter) {
      return clean_value($value, $delimiter);
    }, $array);
    $eol = str_replace($find, $replace, $eol);

    $line = implode($delimiter, $array) . $eol;
    
    $return_value = fputs($fp, $line);

    /** purposefully free up the ram **/
    $line = null;
    $eol = null;
    $array = null;

    /** trigger gc_collect_cycles() every 250th call of this method **/
    if($cycles_count % 250 === 0) gc_collect_cycles();

    return $return_value;
}

/** Use a second function so the GC can be triggered here
  * when it returns the value and all intermediate values are free.
  */
function clean_value($value, $delimeter) 
{
   /**
     *  use static values to prevent reassigning the same
     *  values to the stack over and over
     */
   static $regex = []; 
   static $find = "\r\n";
   static $replace = "\n";
   static $quote = '"';
   if(!isset($regex[$delimeter])) {
      $regex[$delimeter] = "/[$delimiter\"\n\r]/";
   }
   $value = trim($value);
   $value = str_replace($find, $replace, $value);
   if(preg_match($regex[$delimeter], $value)) {
        $value = $quote.str_replace($quote, '""', $value).$quote;
   }
   return $value;
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69131989

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档