当前位置: 首页 > 后端技术 > PHP

Composerautoload自动加载性能优化dump-autoload

时间:2023-03-29 18:27:57 PHP

Composer提供的autoload机制使得我们组织代码和引入新的类库非常方便,但同时也降低了很多项目的性能。composerautoload慢的主要原因是对PSR-0和PSR-4的支持。当加载器得到一个类名时,它需要在文件系统中找到对应的类文件位置。可以看到PSR-4或PSR-0的autoloadLoading是一件很累人的事情。基本上是O(n2)的复杂性。另外还有大量的is_file等IO操作,性能堪忧。那么今天我们就来说说composer自动加载的优化。说之前,先简单说一下composer自动加载的原理。以下是相关部分代码:vendorDir=$vendorDir;}/***找到定义类的文件的路径。**@paramstring$class类的名称**@returnstring|false如果找到则为路径,否则为false*/publicfunctionfindFile($class){//classmaplookupif(isset($this->classMap[$class])){返回$this->classMap[$class];}if($this->classMapAuthoritative||isset($this->missingClasses[$class])){returnfalse;}if(null!==$this->apcuPrefix){$file=apcu_fetch($this->apcuPrefix.$class,$hit);如果($hit){返回$file;$file=$this->findFileWithExtension($class,'.php');//如果我们在HHVM上运行,则搜索Hack文件if(false===$file&&defined('HHVM_VERSION')){$file=$this->findFileWithExtension($class,'.hh');}if(null!==$this->apcuPrefix){apcu_add($this->apcuPrefix.$class,$file);}if(false===$file){//记住这个类不存在。$this->missingClasses[$class]=true;}返回$文件;}privatefunctionfindFileWithExtension($class,$ext){//PSR-4查找$logicalPathPsr4=strtr($class,'\\',DIRECTORY_SEPARATOR)。$分机;$first=$class[0];如果(isset($this->prefixLengthsPsr4[$first])){$subPath=$class;while(false!==$lastPos=strrpos($subPath,'\\')){$subPath=substr($subPath,0,$lastPos);$搜索=$子路径。'\\';如果(isset($this->prefixDirsPsr4[$search])){$pathEnd=DIRECTORY_SEPARATOR.substr($logicalPathPsr4,$lastPos+1);foreach($this->prefixDirsPsr4[$search]as$dir){if(file_exists($file=$dir.$pathEnd)){返回$file;}}}}}//PSR-4后备目录foreach($this->fallbackDirsPsr4as$dir){if(file_exists($file=$dir.DIRECTORY_SEPARATOR.$logicalPathPsr4)){return$file;}}//PSR-0查找if(false!==$pos=strrpos($class,'\\')){//命名空间类名$logicalPathPsr0=substr($logicalPathPsr4,0,$pos+1).strtr(substr($logicalPathPsr4,$pos+1),'_',DIRECTORY_SEPARATOR);}else{//类似PEAR的类名$logicalPathPsr0=strtr($class,'_',DIRECTORY_SEPARATOR).$分机;}if(isset($this->prefixesPsr0[$first])){foreach($this->prefixesPsr0[$first]as$prefix=>$dirs){if(0===strpos($class,$prefix)){foreach($dirsas$dir){if(file_exists($file=$dir.DIRECTORY_SEPARATOR.$logicalPathPsr0)){return$file;}}}}}//PSR-0回退目录foreach($this->fallbackDirsPsr0as$dir){如果(file_exists($file=$dir.DIRECTORY_SEPARATOR.$logicalPathPsr0)){return$file;}}//PSR-0包含路径。如果($this->useIncludePath&&$file=stream_resolve_include_path($logicalPathPsr0)){返回$file;}返回假;}}?>从源码可以看出,Compsoer\ClassLoader会先检查autoload_classmap中生成的所有注册类。如果在类映射中找不到,则回退到psr-4,然后是psr-0。所以我们从类映射开始。1.第一级(Level-1)优化:生成classmap如何运行:执行命令composerdump-autoload-o(-o相当于--optimize)原理:这个命令的本质是将PSR-4/PSR-0规则转换为classmap规则,因为classmap包含了所有类名和类文件路径的对应关系,所以加载器不再需要在文件系统中搜索文件。可以直接从类映射中找到类文件的路径。注意:该命令不考虑在classmap中找不到目标类的情况。当加载器找不到目标类时,它仍然会按照PSR-4/PSR-0的规则在文件系统中搜索。2、二级(Level-2/A)优化:权威(Authoritative)classmap如何运行:执行命令composerdump-autoload-a(-a相当于--classmap-authoritative)执行原理command也隐含执行了Level-1命令,即也生成了classmap。不同的是,当加载器在classmap中找不到目标类时,不会去文件系统中查找(即隐含地认为classmap中包含所有合法类,不会有其他类,除了非法调用)注意事项如果你的项目在运行时生成类,使用这种优化策略将找不到这些新生成的类。3、二级(Level-2/B)优化:使用APCu缓存运行方式:执行命令composerdump-autoload--apcu原理:使用该策略需要安装apcu扩展。Apcu可以理解为一块内存,可以在多个进程间共享。这种策略是在Level-1中classmap中找不到目标类时,将在文件系统中找到的结果存储到共享内存中,下次查找时直接从内存中返回,无需再到文件中查找系统。在生产环境中,该策略一般与Level-1一起使用,执行composerdump-autoload-o--apcu,这样即使在生产环境中生成了一个新类,也只需要在生产环境中查找一次即可待检索文件系统Cache弥补了Level-2/A的缺陷。如何选择优化策略?要根据自己项目的实际情况选择策略,如果你的项目在运行时不生成class文件,需要composer的autoload来加载,那么就用Level-2/A,否则用Level-1和Level-2/B更好的选择。几点提示:1.Level-2优化基本上是Level-1优化的补充。Level-2/A主要是在classmap中找不到目标类时,决定是否继续搜索。Level-2/B主要目的是提供一种缓存机制,当在classmap中找不到时,缓存从文件系统中找到的文件路径,以加快后续的查找。2、Level-2/A执行时??,表示如果在classmap中找不到,就不会继续查找。此时Level-2/B不会生效。3.无论如何,建议开启opcache,这样会大大提高类的加载速度。我可以看到性能至少提高了10倍。