Задать вопрос
@prineside
Спам-бот

PHP — стоит ли использовать кэширование результатов выполнения функций

Доброго времени суток.

Работаю над проектом (PHP), в котором практически все операции происходят над файлами и каталогами — чтение, сканирование, вывод дерева и т.д.
Иногда для создания иерархичного дерева каталога (в смысле, с его подкаталогами) нужно больше 6 секунд (со временем время уменьшается до 3, видимо, кэшируются данные о файлах), что сильно тормозит работу.

Возникла идея кэширования результатов выполнения некоторых функций (тех, что связаны со сканированием подкаталогов) для уменьшения времени выполнения и нагрузки на процессор & хард ценой дискового пространства и оперативной памяти (имхо, 500 кб на харде лучше, чем 6 секунд ждать).

Базу данных не использую (для файлов считаю лишним), поэтому даже не думаю использовать ее в целях оптимизации.

Для реализации идеи написал класс:
<?php
class Cacher {

	// Класс для кэширования данных между скриптами
	// Применять к динамическим данным не рекомендуется
	// Сэкономит время при вызове функций, которые выполняются
	// более 10мс.
	// Принцип действия:
	// Данные выполнения функции кэшируются в файл и в переменную класса
	// Если функция будет вызвана в том же скрипте второй (и больше) раз
	// с теми же параметрами, она сразу вернет данные из кэша
	// Если функция вызвана с какими-то параметрами впервые в скрипте,
	// произойдет проверка на существование файла с кэшем и будет возвращено
	// содержимое файла
	//
	// Использование:
	// 1. Создать объект класса / прописать для своего класса extends Cacher
	// 2. Вызвать Cacher->CacheInit(string cache_dir, string prefix), где указать полный путь
	// 	  к каталогу для кэширования и префикс (опционально, желательно для классов)
	// 3. В начале стабильных функций написать
	// 			/* c */	if($this->IsCached('myfunc')){ return $this->GetCache('myfunc'); }
	// 	  где myfunc - имя ячейки кэша (желательно имя функции)
	// 4. Перед отдачей (return) в функции прописать
	// 	  		/* c */	$this->Cache('myfunc', $ret);
	//    где $ret - результат выполнения функции
	
	public $cache = array();
	public $cache_dir;
	public $prefix;
	public $enabled = false;
	
	function CacheInit($cache_dir, $prefix=''){
		if(!is_dir($cache_dir)){
			mkdir($cache_dir);
		}
		$this->cache_dir = $cache_dir;
		$this->prefix = $prefix;
		$this->enabled = true;
	}
	
	function Cache($function, $data){
		if(!$this->enabled){ return false; }
		$this->cache[$function] = $data;
		file_put_contents($this->cache_dir.$this->prefix.$function,serialize($data));
	}
	
	function GetCache($function){
		if(!$this->enabled){ return false; }
		return $this->cache[$function];
	}
	
	function IsCached($function){
		if(!$this->enabled){ return false; }
		if(isset($this->cache[$function])){
			return true;
		}elseif(is_file($this->cache_dir.$this->prefix.$function)){
			$this->cache[$function] = unserialize(file_get_contents($this->cache_dir.$this->prefix.$function));
			return true;
		}else{
			return false;
		}
	}
	
	function ClearCache($function){
		if(!$this->enabled){ return false; }
		if(is_array($function)){
			foreach($function as $unit){
				@unlink($this->cache_dir.$this->prefix.$unit);
			}
		}else{
			@unlink($this->cache_dir.$this->prefix.$function);
		}
		$this->cache = array();
	}
}
?>


Использовал так:
<?php
class SomeClass extends Cacher {

	function __construct(){
		// Включаем кеширование
		$this->CacheInit(ROOT.'/_cache/vcs/');
		// ...
	}

	function ScanDir($path=''){	// Этим будем вытаскивать дерево каталогов
/* c */	$_c = 'ul('.str_replace('/','_slh_',$path.')';
/* c */	if($this->IsCached($_c)){ return $this->GetCache($_c); }

		$return = array();
		$src = opendir($path);
		while($obj = readdir($src)){
			if(is_dir($path.'/'.$obj)){
				$return = array_merge($return, $this->ScanDir($path.'/'.$obj));	// Глубокая рекурсия
			}else{
				$return[] = $path.'/'.$obj;
			}
		}
		closedir($src);

/* c */ $this->Cache($_c, $return);
		return $return;
	}
}

$Obj = new SomeClass();
$Obj->ScanDir();


Пробовал профилировать выполнение с / без «кэширования» в боевых условиях, результат боевых действий (повторные запросы):

Без «кэширования»: 3.1829 с.
С «кэшированием»: 0.0097 с. +420кб на харде

Вопрос к знатокам: в чем может быть подвох, и оправдан ли такой подход (если кто-то с таким сталкивался), если при изменении файлов / каталогов удалять определенные «ячейки кэша» (удалять файлы)
  • Вопрос задан
  • 8074 просмотра
Подписаться 11 Комментировать
Подписчики вопроса 11 К ответам на вопрос (3)