我在一个类中有两个方法,它们目前共享一个非常相似的实现,但是在性能方面实现成本非常高。
样品:
class Example
{
public void process(Object obj)
{
boolean someFact = getFirstFact(obj);
boolean someOtherFact = getSecondFact(obj);
//use the two facts somehow
}
public boolean getFirstFact(Object obj)
{
boolean data = someExpensiveMethod(obj);
//return some value derived from data
}
public boolean getSecondFact(Object obj)
{
boolean data = someExpensiveMethod(obj);
//return some other value derived from data
}
public boolean someExpensiveMethod(Object obj){...}
}
我曾想过以某种方式缓存someExpensiveMethod的结果,但这看起来很浪费,因为对象倾向于进入,被处理然后被丢弃。它似乎很笨 - 方法需要知道缓存,或者我需要在ssomeExpensiveMethod中缓存结果。
即使是短期缓存也可能是坏消息,因为每天都会有数百万个对象被处理。
我的担忧是双重的 - 首先,不能保证这两种方法总是依赖于第三种方法,所以任何解决方案都应该从他们的POV透明,其次,明显的解决方案(在某些昂贵的方法中)可能非常在空间方面成本高昂,不需要长期保存。
答案 0 :(得分:3)
我曾想过以某种方式缓存
someExpensiveMethod
的结果,但这看起来很浪费,因为对象倾向于进入,被处理然后被丢弃。
我不明白这是多么浪费。这基本上就是缓存的工作方式。您将进入的对象与您最近处理的对象进行比较,当您获得“点击”时,可以避免调用someExpensiveMethod
的费用。
缓存实际上是否适用于您的应用程序取决于许多因素,如:
someExpensiveMethod
(最后一点难以预测/测量,但它包含了表示缓存结构所需的额外内存,GC必须采取的工作来处理缓存及其内容“可达”的事实,和弱引用相关的GC开销......假设你使用它们。)
最终,缓存解决方案的成功(或其他方面)将根据系统对实际工作负载的平均行为进行判断。一些缓存结果从未再次使用的事实并不真正相关。
它似乎很笨 - 方法需要了解缓存,或者我需要在
someExpensiveMethod
中缓存结果。
同样,IMO不是“笨重”的。这是您实现缓存的方式。
即使是短期缓存也可能是坏消息,因为每天都会有数百万个对象被处理。
同样,我没有看到你论证的逻辑。如果每天处理数百万个对象并保持(比方说)最后5分钟的价值,那么只需要数万个对象进行缓存。这不是“坏消息”。
如果真的每天处理“数百万”对象,那么:
someExpensiveMethod
不能那么昂贵......除非你高效缓存和大量内存或大量处理器, 或两者,答案 1 :(得分:0)
你总是在调用流程方法(我的意思是,你是不是直接调用get ... Fact方法)?如果是这种情况,那么您肯定知道在getSecondFact之前总是调用getFirstFact。
然后,您可以使用私有字段在getFirstFact方法中缓存someExpensiveMethod的布尔输出,并在getSecondFact方法中重用该值:
class Example
{
private boolean _expensiveMethodOutput;
public void process(Object obj)
{
boolean someFact = getFirstFact(obj);
boolean someOtherFact = getSecondFact(obj);
//use the two facts somehow
}
private boolean getFirstFact(Object obj)
{
_expensiveMethodOutput = someExpensiveMethod(obj);
//return some value derived from data
}
private boolean getSecondFact(Object obj)
{
boolean data = _expensiveMethodOutput;
//return some other value derived from data
}
private boolean someExpensiveMethod(Object obj){...}
}
答案 2 :(得分:0)
从你的问题标题我想你不想做
class Example
{
public void process(Object obj)
{
boolean expensiveResult = someExpensiveMethod(obj);
boolean someFact = getFirstFact(expensiveResult);
boolean someOtherFact = getSecondFact(expensiveResult);
//use the two facts somehow
}
...
因为这意味着在更改其中一种方法时,您无法再访问obj
。此外,您希望尽可能避免执行昂贵的方法。一个简单的解决方案是
private Object lastParam = null;
private boolean lastResult = false;
public boolean someExpensiveMethod(Object obj){
if (obj == lastParam) return lastResult;
lastResult = actualExpensiveMethod(obj);
lastParam = obj;
return lastResult ;
}
当然这不适用于多线程。 (至少确保process
已同步。)
答案 3 :(得分:0)
我会考虑引入一个工厂方法和一个封装预处理的新对象。这样,只要对象超出范围,jvm就可以丢弃预处理的数据。
class PreprocessedObject {
private ... data;
public static PreprocessedObject create(Object obj) {
PreprocessedObject pObj = new PreprocessedObject();
// do expensive stuff
pObj.data = ...
return pObj;
}
public boolean getFirstFact() {
//return some value derived from data
}
public boolean getSecondFact() {
//return some other value derived from data
}
}
答案 4 :(得分:0)
除了Stephen的回答,我建议你看一下Google Guava。有一个计算地图的概念适合您在这里遇到的问题。我写了一篇关于here的文章。
在代码方面,我建议这样做:
class Example {
private ConcurrentMap<Object, Boolean> cache;
void initCache() {
cache = new MapMaker().softValues()
.makeComputingMap(new Function<Object, Boolean>() {
@Override
public Boolean apply(Object from) {
return someExpensiveMethod(from);
}
});
}
public void process(Object obj) {
boolean someFact = getFirstFact(obj);
boolean someOtherFact = getSecondFact(obj);
// use the two facts somehow
}
public boolean getFirstFact(Object obj) {
boolean data = cache.get(obj);
// return some value derived from data
}
public boolean getSecondFact(Object obj) {
boolean data = cache.get(obj);
// return some other value derived from data
}
public boolean someExpensiveMethod(Object obj) {
}
}