Android Dagger2 框架依赖图构建模块深度剖析(三)

devtools/2025/3/16 3:39:10/

一、引言

在 Android 开发中,依赖注入(Dependency Injection,简称 DI)是一种重要的设计模式,它能够降低代码的耦合度,提高代码的可测试性和可维护性。Dagger 2 作为一款高效的依赖注入框架,在编译时生成依赖注入代码,避免了运行时反射带来的性能开销。其中,依赖图构建模块是 Dagger 2 的核心组成部分,它负责解析依赖关系,构建依赖图,为后续的依赖注入提供基础。本文将深入分析 Dagger 2 框架的依赖图构建模块,从源码级别详细介绍其工作原理和实现细节。

二、依赖图构建模块概述

2.1 依赖图的概念

依赖图是一种有向图,用于表示对象之间的依赖关系。在 Dagger 2 中,依赖图的节点表示依赖对象,边表示依赖关系。通过构建依赖图,可以清晰地了解各个依赖对象之间的依赖关系,从而实现正确的依赖注入。

2.2 依赖图构建模块的作用

依赖图构建模块的主要作用是解析 Dagger 2 注解(如@Inject@Module@Provides@Component等),并根据注解信息构建依赖图。在构建过程中,会检查依赖关系的合法性,处理循环依赖等问题,确保依赖图的正确性。

2.3 依赖图构建模块的工作流程

依赖图构建模块的工作流程主要包括以下几个步骤:

  1. 注解扫描:扫描源代码中的 Dagger 2 注解,收集依赖信息。
  2. 依赖解析:根据注解信息,解析各个依赖对象之间的依赖关系。
  3. 图构建:将解析得到的依赖关系转化为依赖图。
  4. 图验证:检查依赖图的合法性,处理循环依赖等问题。
  5. 代码生成:根据依赖图生成依赖注入代码。

三、注解扫描

3.1 注解处理器

Dagger 2 使用注解处理器在编译时处理注解。注解处理器是一个实现了javax.annotation.processing.AbstractProcessor接口的类,它会在编译过程中扫描源代码中的注解,并根据注解信息进行相应的处理。以下是一个简化的注解处理器示例:

java

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;// 定义支持的注解类型
@SupportedAnnotationTypes({"javax.inject.Inject","dagger.Module","dagger.Provides","dagger.Component"
})
// 定义支持的源代码版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DaggerProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {// 遍历所有支持的注解类型for (TypeElement annotation : annotations) {// 获取被该注解标记的所有元素Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);for (Element element : annotatedElements) {// 处理注解元素processAnnotatedElement(element, annotation);}}return true;}private void processAnnotatedElement(Element element, TypeElement annotation) {// 根据注解类型进行不同的处理if (annotation.getQualifiedName().contentEquals("javax.inject.Inject")) {// 处理 @Inject 注解processInjectAnnotation(element);} else if (annotation.getQualifiedName().contentEquals("dagger.Module")) {// 处理 @Module 注解processModuleAnnotation(element);} else if (annotation.getQualifiedName().contentEquals("dagger.Provides")) {// 处理 @Provides 注解processProvidesAnnotation(element);} else if (annotation.getQualifiedName().contentEquals("dagger.Component")) {// 处理 @Component 注解processComponentAnnotation(element);}}private void processInjectAnnotation(Element element) {// 处理 @Inject 注解的具体逻辑// 例如,记录需要注入的字段或构造函数}private void processModuleAnnotation(Element element) {// 处理 @Module 注解的具体逻辑// 例如,记录模块类和提供依赖的方法}private void processProvidesAnnotation(Element element) {// 处理 @Provides 注解的具体逻辑// 例如,记录提供依赖的方法和返回类型}private void processComponentAnnotation(Element element) {// 处理 @Component 注解的具体逻辑// 例如,记录组件类和依赖的模块}
}

3.2 注解信息收集

在注解处理器中,会收集各个注解的信息,包括注解标记的元素、注解的属性等。例如,对于@Inject注解,会记录需要注入的字段或构造函数;对于@Module注解,会记录模块类和提供依赖的方法;对于@Provides注解,会记录提供依赖的方法和返回类型;对于@Component注解,会记录组件类和依赖的模块。以下是一个简化的注解信息收集示例:

java

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import java.util.ArrayList;
import java.util.List;// 注解信息收集器
class AnnotationInfoCollector {private List<Element> injectElements = new ArrayList<>();private List<TypeElement> moduleElements = new ArrayList<>();private List<ExecutableElement> providesElements = new ArrayList<>();private List<TypeElement> componentElements = new ArrayList<>();public void collectInjectElement(Element element) {injectElements.add(element);}public void collectModuleElement(TypeElement element) {moduleElements.add(element);}public void collectProvidesElement(ExecutableElement element) {providesElements.add(element);}public void collectComponentElement(TypeElement element) {componentElements.add(element);}public List<Element> getInjectElements() {return injectElements;}public List<TypeElement> getModuleElements() {return moduleElements;}public List<ExecutableElement> getProvidesElements() {return providesElements;}public List<TypeElement> getComponentElements() {return componentElements;}
}

3.3 注解扫描的实现细节

在注解处理器的process方法中,会遍历所有支持的注解类型,并获取被该注解标记的所有元素。然后,调用processAnnotatedElement方法对每个注解元素进行处理。在processAnnotatedElement方法中,根据注解类型调用不同的处理方法,如processInjectAnnotationprocessModuleAnnotation等。在这些处理方法中,会将注解信息收集到AnnotationInfoCollector中。以下是一个完整的注解扫描示例:

java

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import java.util.Set;// 定义支持的注解类型
@SupportedAnnotationTypes({"javax.inject.Inject","dagger.Module","dagger.Provides","dagger.Component"
})
// 定义支持的源代码版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DaggerProcessor extends AbstractProcessor {private AnnotationInfoCollector infoCollector = new AnnotationInfoCollector();@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {// 遍历所有支持的注解类型for (TypeElement annotation : annotations) {// 获取被该注解标记的所有元素Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);for (Element element : annotatedElements) {// 处理注解元素processAnnotatedElement(element, annotation);}}return true;}private void processAnnotatedElement(Element element, TypeElement annotation) {// 根据注解类型进行不同的处理if (annotation.getQualifiedName().contentEquals("javax.inject.Inject")) {// 处理 @Inject 注解infoCollector.collectInjectElement(element);} else if (annotation.getQualifiedName().contentEquals("dagger.Module")) {// 处理 @Module 注解infoCollector.collectModuleElement((TypeElement) element);} else if (annotation.getQualifiedName().contentEquals("dagger.Provides")) {// 处理 @Provides 注解infoCollector.collectProvidesElement((ExecutableElement) element);} else if (annotation.getQualifiedName().contentEquals("dagger.Component")) {// 处理 @Component 注解infoCollector.collectComponentElement((TypeElement) element);}}public AnnotationInfoCollector getInfoCollector() {return infoCollector;}
}

四、依赖解析

4.1 依赖关系的表示

在 Dagger 2 中,依赖关系可以通过Dependency类来表示。Dependency类包含了依赖对象的类型和提供依赖的方式。以下是一个简化的Dependency类示例:

java

import javax.lang.model.type.TypeMirror;// 依赖关系类
class Dependency {private TypeMirror type; // 依赖对象的类型private boolean isProvidedByModule; // 是否由模块提供依赖public Dependency(TypeMirror type, boolean isProvidedByModule) {this.type = type;this.isProvidedByModule = isProvidedByModule;}public TypeMirror getType() {return type;}public boolean isProvidedByModule() {return isProvidedByModule;}
}

4.2 依赖解析的过程

依赖解析的过程主要是根据注解信息,确定各个依赖对象之间的依赖关系。具体步骤如下:

  1. 解析@Inject注解:对于被@Inject注解标记的字段或构造函数,确定其依赖的对象类型。

  2. 解析@Module@Provides注解:对于被@Module注解标记的模块类,解析其中被@Provides注解标记的方法,确定这些方法提供的依赖对象类型。

  3. 建立依赖关系:根据解析得到的信息,建立各个依赖对象之间的依赖关系。以下是一个简化的依赖解析示例:

java

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;// 依赖解析器
class DependencyResolver {private AnnotationInfoCollector infoCollector;private Map<TypeMirror, List<Dependency>> dependencyMap = new HashMap<>();public DependencyResolver(AnnotationInfoCollector infoCollector) {this.infoCollector = infoCollector;}public void resolveDependencies() {// 解析 @Inject 注解resolveInjectAnnotations();// 解析 @Module 和 @Provides 注解resolveModuleAndProvidesAnnotations();}private void resolveInjectAnnotations() {List<Element> injectElements = infoCollector.getInjectElements();for (Element element : injectElements) {if (element.getKind().isField()) {// 处理字段注入TypeMirror fieldType = element.asType();Dependency dependency = new Dependency(fieldType, false);addDependency(fieldType, dependency);} else if (element.getKind().isConstructor()) {// 处理构造函数注入ExecutableElement constructor = (ExecutableElement) element;for (Element parameter : constructor.getParameters()) {TypeMirror parameterType = parameter.asType();Dependency dependency = new Dependency(parameterType, false);addDependency(parameterType, dependency);}}}}private void resolveModuleAndProvidesAnnotations() {List<ExecutableElement> providesElements = infoCollector.getProvidesElements();for (ExecutableElement providesElement : providesElements) {TypeMirror returnType = providesElement.getReturnType();Dependency dependency = new Dependency(returnType, true);addDependency(returnType, dependency);}}private void addDependency(TypeMirror type, Dependency dependency) {List<Dependency> dependencies = dependencyMap.computeIfAbsent(type, k -> new ArrayList<>());dependencies.add(dependency);}public Map<TypeMirror, List<Dependency>> getDependencyMap() {return dependencyMap;}
}

4.3 依赖解析的实现细节

DependencyResolver类中,resolveDependencies方法是依赖解析的入口。它会依次调用resolveInjectAnnotationsresolveModuleAndProvidesAnnotations方法,分别解析@Inject注解和@Module@Provides注解。在resolveInjectAnnotations方法中,会处理被@Inject注解标记的字段和构造函数,确定其依赖的对象类型,并将依赖信息添加到dependencyMap中。在resolveModuleAndProvidesAnnotations方法中,会处理被@Provides注解标记的方法,确定这些方法提供的依赖对象类型,并将依赖信息添加到dependencyMap中。

五、图构建

5.1 图的表示

在 Dagger 2 中,依赖图可以通过DependencyGraph类来表示。DependencyGraph类包含了图的节点和边的信息。以下是一个简化的DependencyGraph类示例:

java

import javax.lang.model.type.TypeMirror;
import java.util.*;// 依赖图类
class DependencyGraph {private Map<TypeMirror, List<TypeMirror>> adjacencyList = new HashMap<>(); // 邻接表表示图public void addNode(TypeMirror node) {adjacencyList.putIfAbsent(node, new ArrayList<>());}public void addEdge(TypeMirror from, TypeMirror to) {adjacencyList.computeIfAbsent(from, k -> new ArrayList<>()).add(to);}public List<TypeMirror> getNeighbors(TypeMirror node) {return adjacencyList.getOrDefault(node, Collections.emptyList());}public Set<TypeMirror> getNodes() {return adjacencyList.keySet();}
}

5.2 图构建的过程

图构建的过程主要是根据依赖解析得到的依赖关系,将其转化为依赖图。具体步骤如下:

  1. 添加节点:将所有依赖对象的类型作为节点添加到图中。

  2. 添加边:根据依赖关系,在图中添加边,表示依赖关系。以下是一个简化的图构建示例:

java

import javax.lang.model.type.TypeMirror;
import java.util.Map;
import java.util.List;// 图构建器
class GraphBuilder {private DependencyResolver resolver;private DependencyGraph graph = new DependencyGraph();public GraphBuilder(DependencyResolver resolver) {this.resolver = resolver;}public void buildGraph() {Map<TypeMirror, List<Dependency>> dependencyMap = resolver.getDependencyMap();// 添加节点for (TypeMirror type : dependencyMap.keySet()) {graph.addNode(type);}// 添加边for (Map.Entry<TypeMirror, List<Dependency>> entry : dependencyMap.entrySet()) {TypeMirror from = entry.getKey();List<Dependency> dependencies = entry.getValue();for (Dependency dependency : dependencies) {TypeMirror to = dependency.getType();graph.addEdge(from, to);}}}public DependencyGraph getGraph() {return graph;}
}

5.3 图构建的实现细节

GraphBuilder类中,buildGraph方法是图构建的入口。它会首先获取依赖解析得到的dependencyMap,然后遍历dependencyMap的键,将所有依赖对象的类型作为节点添加到图中。接着,遍历dependencyMap的每个条目,根据依赖关系在图中添加边。

六、图验证

6.1 循环依赖的检测

循环依赖是指依赖关系中存在环路,即 A 依赖 B,B 又依赖 A。循环依赖会导致依赖注入无法正常进行,因此需要在图构建完成后进行循环依赖的检测。可以使用深度优先搜索(DFS)算法来检测循环依赖。以下是一个简化的循环依赖检测示例:

java

import javax.lang.model.type.TypeMirror;
import java.util.*;// 循环依赖检测器
class CycleDetector {private DependencyGraph graph;private Set<TypeMirror> visited = new HashSet<>();private Set<TypeMirror> recursionStack = new HashSet<>();public CycleDetector(DependencyGraph graph) {this.graph = graph;}public boolean hasCycle() {for (TypeMirror node : graph.getNodes()) {if (!visited.contains(node)) {if (dfs(node)) {return true;}}}return false;}private boolean dfs(TypeMirror node) {visited.add(node);recursionStack.add(node);for (TypeMirror neighbor : graph.getNeighbors(node)) {if (!visited.contains(neighbor)) {if (dfs(neighbor)) {return true;}} else if (recursionStack.contains(neighbor)) {return true;}}recursionStack.remove(node);return false;}
}

6.2 图验证的过程

图验证的过程主要是检测依赖图中是否存在循环依赖。如果存在循环依赖,会抛出异常,提示开发者解决循环依赖问题。以下是一个简化的图验证示例:

java

// 图验证器
class GraphValidator {private DependencyGraph graph;public GraphValidator(DependencyGraph graph) {this.graph = graph;}public void validateGraph() {CycleDetector detector = new CycleDetector(graph);if (detector.hasCycle()) {throw new IllegalStateException("Dependency graph contains a cycle!");}}
}

6.3 图验证的实现细节

GraphValidator类中,validateGraph方法是图验证的入口。它会创建一个CycleDetector对象,并调用其hasCycle方法检测依赖图中是否存在循环依赖。如果存在循环依赖,会抛出IllegalStateException异常。

七、代码生成

7.1 代码生成的目标

代码生成的目标是根据依赖图生成依赖注入代码,实现依赖对象的创建和注入。生成的代码通常包括组件类的实现、模块类的包装类、依赖提供者类等。

7.2 代码生成的过程

代码生成的过程主要包括以下几个步骤:

  1. 确定生成的代码结构:根据依赖图和注解信息,确定生成的代码结构,如组件类的接口和实现、模块类的包装类、依赖提供者类等。

  2. 生成代码模板:根据代码结构,生成代码模板,包括类的定义、方法的定义、字段的定义等。

  3. 填充代码模板:根据依赖图和注解信息,填充代码模板,生成具体的代码。以下是一个简化的代码生成示例:

java

import javax.lang.model.type.TypeMirror;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.List;// 代码生成器
class CodeGenerator {private DependencyGraph graph;private Map<TypeMirror, List<Dependency>> dependencyMap;public CodeGenerator(DependencyGraph graph, Map<TypeMirror, List<Dependency>> dependencyMap) {this.graph = graph;this.dependencyMap = dependencyMap;}public void generateCode() {// 生成组件类的实现generateComponentImplementation();// 生成模块类的包装类generateModuleWrappers();// 生成依赖提供者类generateDependencyProviders();}private void generateComponentImplementation() {// 生成组件类的实现代码try (FileWriter writer = new FileWriter("ComponentImpl.java")) {writer.write("public class ComponentImpl {\n");// 生成组件类的字段和方法for (TypeMirror node : graph.getNodes()) {writer.write("    private " + node.toString() + " " + node.toString().toLowerCase() + ";\n");}writer.write("    public ComponentImpl() {\n");// 生成组件类的构造函数代码for (TypeMirror node : graph.getNodes()) {writer.write("        this." + node.toString().toLowerCase() + " = new " + node.toString() + "();\n");}writer.write("    }\n");writer.write("}\n");} catch (IOException e) {e.printStackTrace();}}private void generateModuleWrappers() {// 生成模块类的包装类代码// 具体实现省略}private void generateDependencyProviders() {// 生成依赖提供者类代码// 具体实现省略}
}

7.3 代码生成的实现细节

CodeGenerator类中,generateCode方法是代码生成的入口。它会依次调用generateComponentImplementationgenerateModuleWrappersgenerateDependencyProviders方法,分别生成组件类的实现、模块类的包装类和依赖提供者类的代码。在generateComponentImplementation方法中,会创建一个FileWriter对象,将生成的组件类实现代码写入文件。

八、依赖图构建模块的整体流程

8.1 整体流程概述

依赖图构建模块的整体流程包括注解扫描、依赖解析、图构建、图验证和代码生成。以下是一个简化的整体流程示例:

java

// 依赖图构建模块的整体流程示例
public class DependencyGraphBuilder {public static void main(String[] args) {// 创建注解处理器DaggerProcessor processor = new DaggerProcessor();// 模拟注解扫描过程// 这里省略具体的注解扫描代码,假设已经完成注解扫描// 获取注解信息收集器AnnotationInfoCollector infoCollector = processor.getInfoCollector();// 创建依赖解析器DependencyResolver resolver = new DependencyResolver(infoCollector);// 解析依赖关系resolver.resolveDependencies();// 创建图构建器GraphBuilder graphBuilder = new GraphBuilder(resolver);// 构建依赖图graphBuilder.buildGraph();// 获取依赖图DependencyGraph graph = graphBuilder.getGraph();// 创建图验证器GraphValidator validator = new GraphValidator(graph);try {// 验证依赖图validator.validateGraph();// 创建代码生成器CodeGenerator generator = new CodeGenerator(graph, resolver.getDependencyMap());// 生成代码generator.generateCode();} catch (IllegalStateException e) {System.err.println("Error: " + e.getMessage());}}
}

8.2 整体流程的实现细节

DependencyGraphBuilder类的main方法中,首先创建了一个DaggerProcessor对象,模拟注解扫描过程。然后获取注解信息收集器,创建DependencyResolver对象,解析依赖关系。接着创建GraphBuilder对象,构建依赖图。再创建GraphValidator对象,验证依赖图。如果依赖图验证通过,创建CodeGenerator对象,生成代码。如果依赖图存在循环依赖,会捕获IllegalStateException异常,并输出错误信息。

九、依赖图构建模块的优化

9.1 性能优化

  • 缓存机制:在依赖解析和图构建过程中,可以使用缓存机制来避免重复计算。例如,对于已经解析过的依赖关系,可以将其缓存起来,下次需要时直接从缓存中获取。
  • 并行处理:对于大规模的项目,可以考虑使用并行处理来提高依赖图构建的性能。例如,将注解扫描、依赖解析、图构建等步骤并行执行。

9.2 错误处理优化

  • 详细的错误信息:在图验证过程中,如果发现循环依赖或其他错误,应该提供详细的错误信息,帮助开发者快速定位和解决问题。
  • 错误恢复机制:在代码生成过程中,如果出现错误,应该有错误恢复机制,避免程序崩溃。例如,可以捕获异常,记录错误信息,并尝试继续生成其他部分的代码。

9.3 代码生成优化

  • 代码模板优化:可以使用更灵活的代码模板,根据不同的依赖关系和注解信息生成更优化的代码。例如,对于单例对象,可以生成单例模式的代码。
  • 代码压缩:生成的代码可能会比较冗长,可以使用代码压缩工具对生成的代码进行压缩,减少代码体积。

十、总结

本文深入分析了 Android Dagger 2 框架的依赖图构建模块,从源码级别详细介绍了其工作原理和实现细节。依赖图构建模块是 Dagger 2 的核心组成部分,它通过注解扫描、依赖解析、图构建、图验证和代码生成等步骤,实现了依赖关系的解析和依赖注入代码的生成。在实际开发中,掌握依赖图构建模块的原理和实现细节,有助于更好地使用 Dagger 2 进行依赖注入,提高代码的可测试性和可维护性。同时,通过对依赖图构建模块的优化,可以进一步提高其性能和可靠性。

以上是一篇关于 Android Dagger 2 框架依赖图构建模块的技术博客,涵盖了从注解扫描到代码生成的整个流程,以及相关的优化建议。希望对你有所帮助!


http://www.ppmy.cn/devtools/167447.html

相关文章

c_cpp_properties.json等三个文件解释

不建议太小白的人看啊 在 Visual Studio Code 中使用 C 语言进行编程时&#xff0c;通常会看到一些特定的配置文件。这些文件是用来帮助你配置开发环境、调试程序等 就是这三个文件 首先是c_cpp_properties.json&#xff1a; 这是 Visual Studio Code 配置 C/C 开发环境的文件。…

[目标检测] 训练之前要做什么

背景&#xff1a;训练一个Yolo8模型&#xff0c;在训练之前&#xff0c;数据集的处理是影响效果的关键因素。 Step1 定义规则 什么是人/车&#xff0c;比如人的话可能是站着的人&#xff0c;如果是骑电动车/自行车就不算是人。 Step2 收集数据集 1. 自己标注。如果是自己标…

机器人触觉的意义

机器人触觉的重要性 触觉在机器人领域至关重要&#xff0c;尤其是在自主操作、精细操控、人机交互等方面。虽然视觉和语音技术已高度发展&#xff0c;但机器人在现实世界中的操作仍然受限&#xff0c;因为&#xff1a; 视觉有局限性&#xff1a;仅凭视觉&#xff0c;机器人难…

碰一碰发视频源码搭建,碰一碰发视频私有化部署,碰一碰发视频OEM贴牌

引言 随着移动互联网的快速发展&#xff0c;短视频应用成为了用户日常娱乐和信息获取的重要方式。碰一碰发视频功能作为一种新颖的交互方式&#xff0c;能够通过设备之间的简单触碰实现视频的快速分享。本文将详细介绍如何搭建碰一碰发视频的源码&#xff0c;并进行私有化部署…

解决跨域问题的6种方案

解决跨域问题&#xff08;Cross-Origin Resource Sharing, CORS&#xff09;是 Web 开发中常见的需求&#xff0c;以下是 6 种主流解决方案&#xff0c;涵盖前端、后端和服务器配置等不同层面&#xff1a; 一、CORS&#xff08;跨域资源共享&#xff09; 原理 通过服务器设置…

贪心算法简介(greed)

前言&#xff1a; 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在每个决策阶段都选择当前最优解的算法策略&#xff0c;通过局部最优的累积来寻求全局最优解。其本质是"短视"策略&#xff0c;不回溯已做选择。 什么是贪心、如何来理解贪心(个人对贪心的…

conda install 和 pip install 的区别

conda install 和 pip install 是两个常用的包安装命令&#xff0c;但它们在很多方面存在差异。 1. 所属管理系统不同 1.1 conda install conda install 是Anaconda和Miniconda发行版自带的包管理工具 conda 的安装命令。conda 是一个跨平台的开源包管理系统和环境管理系统&…

大语言模型基础之‘显存优化‘

上一篇可扩展的训练技术(二)中&#xff0c;我们介绍了零冗余优化器&#xff08;Zero Redundancy Optimizer, Zero&#xff09;&#xff0c;该技术由DeepSpeed代码库提出&#xff0c;主要用于解决数据并行中的模型冗余技术&#xff0c;即在数据并行训练中&#xff0c;每个GPU上都…