代码设计:性能与可维护性
Code design: performance vs maintainability
情境化
我正在测试上下文中使用 soot 框架实现字节码检测器,我想知道哪种设计更好。
我正在为我正在检测的 Class 中的每个方法构建 TraceMethod 对象,我想 运行 在多个 Class 上使用此检测器。
哪个选项提供更多性能(Space–时间)?
选项 1:(地图)
public class TraceMethod {
boolean[] decisionNodeList;
boolean[] targetList;
Map<Integer,List<Integer>> dependenciesMap;
Map<Integer,List<Double>> decisionNodeBranchDistance;
}
选项 2:(对象)
public class TraceMethod {
ArrayList<Target> targets = new ArrayList<Target>();
ArrayList<DecisionNode> decisionNodes = new ArrayList<DecisionNode>();
}
public class DecisionNode {
int id;
Double branchDistance;
boolean reached;
}
public class Target {
int id;
boolean reached;
List<DecisionNode> dependencies;
}
我自己实施了 选项 2,但我的老板建议我使用 选项 1,他认为那是 "lighter".我在这篇文章“Class Object vs Hashmap”中看到 HashMap 使用的内存比对象多,但我仍然不相信我的解决方案(选项 2)更好。
这是一个简单的细节,但我想确保我使用的是最佳解决方案,我关心的是性能(Space–时间)。我知道第二种选择在可维护性方面更好,但如果它不是最优的,我可以牺牲它。
方法 1 有可能更快并且使用更少 space。
特别是对于字节码工具,我会首先实现方法 1。
然后当它工作时,将两个列表替换为使用原始类型而不是 Integer 和 Double object 的非泛型列表。
请注意,int 需要 4 个字节,而 Integer (Object) 需要 16 - 20 个字节,具体取决于机器(PC 为 16,android 为 20)。
列表可以替换为使用原始整数的GrowingIntArray
(如果我没记错的话,我在Apache的统计包中找到了)。 (或者一旦你知道内容不能再改变,可能只是被一个 int[] 取代)
然后你只需编写自己的 GrowingDoubleArray(或使用 double[])
记住 Collections 方便但速度较慢。
Objects 使用的 space 是原语的 4 倍。
字节码检测器需要性能,它不是每周 运行 一次的软件。
最后我不会用非通用的地图替换那个地图,这似乎
对我来说很多工作。但您可以在最后一步尝试。
作为最后的优化步骤:查看您的列表或地图中有多少元素。如果通常小于 16(你必须尝试一下),你可以切换到线性搜索,
对于非常少的元素,这是最快的。
一旦元素数量超过特定数量,您甚至可以让您的代码智能地切换搜索算法。
(Sun/Oracle java 在他们的一些 Collections 中这样做,并且 Apple/ios,到)。
然而,这最后一步会使您的代码复杂得多。
Space 例如:
DecisionNode:16 代表 class + 4 (id) + 20 (Double) +4 (boolean) = 44 + 4 padding to then next multiple of 8 = 48 bytes.
一般来说,您应该总是进行维护,而不是为了预期的性能。这有几个很好的理由:
- 我们往往对数组与 HashMap 之间的速度差异着迷,但在实际的企业应用程序中,这些差异不足以说明应用程序速度的明显差异。
- 应用程序中最常见的瓶颈在数据库或网络中。
- JVM 在一定程度上优化了代码
由于可维护的代码,您的应用程序不太可能出现性能问题。更有可能的情况是,当您拥有数百万行无法维护的代码时,您的老板会 运行 没钱了。
情境化
我正在测试上下文中使用 soot 框架实现字节码检测器,我想知道哪种设计更好。
我正在为我正在检测的 Class 中的每个方法构建 TraceMethod 对象,我想 运行 在多个 Class 上使用此检测器。
哪个选项提供更多性能(Space–时间)?
选项 1:(地图)
public class TraceMethod {
boolean[] decisionNodeList;
boolean[] targetList;
Map<Integer,List<Integer>> dependenciesMap;
Map<Integer,List<Double>> decisionNodeBranchDistance;
}
选项 2:(对象)
public class TraceMethod {
ArrayList<Target> targets = new ArrayList<Target>();
ArrayList<DecisionNode> decisionNodes = new ArrayList<DecisionNode>();
}
public class DecisionNode {
int id;
Double branchDistance;
boolean reached;
}
public class Target {
int id;
boolean reached;
List<DecisionNode> dependencies;
}
我自己实施了 选项 2,但我的老板建议我使用 选项 1,他认为那是 "lighter".我在这篇文章“Class Object vs Hashmap”中看到 HashMap 使用的内存比对象多,但我仍然不相信我的解决方案(选项 2)更好。
这是一个简单的细节,但我想确保我使用的是最佳解决方案,我关心的是性能(Space–时间)。我知道第二种选择在可维护性方面更好,但如果它不是最优的,我可以牺牲它。
方法 1 有可能更快并且使用更少 space。
特别是对于字节码工具,我会首先实现方法 1。
然后当它工作时,将两个列表替换为使用原始类型而不是 Integer 和 Double object 的非泛型列表。
请注意,int 需要 4 个字节,而 Integer (Object) 需要 16 - 20 个字节,具体取决于机器(PC 为 16,android 为 20)。
列表可以替换为使用原始整数的GrowingIntArray
(如果我没记错的话,我在Apache的统计包中找到了)。 (或者一旦你知道内容不能再改变,可能只是被一个 int[] 取代)
然后你只需编写自己的 GrowingDoubleArray(或使用 double[])
记住 Collections 方便但速度较慢。
Objects 使用的 space 是原语的 4 倍。
字节码检测器需要性能,它不是每周 运行 一次的软件。
最后我不会用非通用的地图替换那个地图,这似乎 对我来说很多工作。但您可以在最后一步尝试。
作为最后的优化步骤:查看您的列表或地图中有多少元素。如果通常小于 16(你必须尝试一下),你可以切换到线性搜索, 对于非常少的元素,这是最快的。 一旦元素数量超过特定数量,您甚至可以让您的代码智能地切换搜索算法。 (Sun/Oracle java 在他们的一些 Collections 中这样做,并且 Apple/ios,到)。 然而,这最后一步会使您的代码复杂得多。
Space 例如:
DecisionNode:16 代表 class + 4 (id) + 20 (Double) +4 (boolean) = 44 + 4 padding to then next multiple of 8 = 48 bytes.
一般来说,您应该总是进行维护,而不是为了预期的性能。这有几个很好的理由:
- 我们往往对数组与 HashMap 之间的速度差异着迷,但在实际的企业应用程序中,这些差异不足以说明应用程序速度的明显差异。
- 应用程序中最常见的瓶颈在数据库或网络中。
- JVM 在一定程度上优化了代码
由于可维护的代码,您的应用程序不太可能出现性能问题。更有可能的情况是,当您拥有数百万行无法维护的代码时,您的老板会 运行 没钱了。