Skip to content

Commit

Permalink
Merge pull request bytedance#57 from bytedance/yangzhiqian/master
Browse files Browse the repository at this point in the history
merge internal:Cache graph nodes with daemon
  • Loading branch information
yangzhiqian authored Sep 4, 2020
2 parents 31cc388 + 0c4ae85 commit 749d951
Show file tree
Hide file tree
Showing 24 changed files with 284 additions and 143 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Change Log
### Version 0.2.1
> optimize incremental build:Cache graph nodes with daemon
### Version 0.1.9
- optimize refer checker:More detailed error information
- add [ByteXBuildListener](wiki/ByteX-Developer-API-en.md#perceive-the-lifecycle-of-bytex)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Add those configuration code to your build.gradle, and apply your plugins on dem

```groovy
buildscript {
ext.plugin_version="0.1.9"
ext.plugin_version="0.2.1"
repositories {
google()
jcenter()
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ ByteX是一个基于gradle transform api和ASM的字节码插件平台(或许

```groovy
buildscript {
ext.plugin_version="0.1.9"
ext.plugin_version="0.2.1"
repositories {
google()
jcenter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ public enum BooleanProperty implements Property<Boolean> {
ENABLE_DUPLICATE_CLASS_CHECK("bytex.enableDuplicateClassCheck", true),
ENABLE_HTML_LOG("bytex.enableHtmlLog", true),
ENABLE_RAM_CACHE("bytex.enableRAMCache", true),
ENABLE_ASYNC_LOAD_CACHE("bytex.asyncLoadCache", true),
ENABLE_RAM_NODES_CACHE("bytex.enableRAMNodesCache", true),
ENABLE_RAM_CLASSES_CACHE("bytex.enableRAMClassesCache", false),
ENABLE_ASYNC_SAVE_CACHE("bytex.asyncSaveCache", true),
ENABLE_VERIFY_PROGUARD_CONFIGURATION_CHANGED("bytex.verifyProguardConfigurationChanged", true),
CHECK_INCREMENTAL_INDEBUG("bytex.checkIncrementalInDebug", false),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.ss.android.ugc.bytex.common.graph

import java.util.concurrent.ConcurrentHashMap

/**
* Created by yangzhiqian on 2020/9/3<br/>
*/
internal class EditableGraph(map: Map<String, Node>) : Graph(map) {
/**
* clear graph info
* called by internal,please do not call.
*/
fun clear() {
nodeMap = ConcurrentHashMap()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,63 +18,12 @@
public class Graph {


private final Map<String, Node> nodeMap;
protected Map<String, Node> nodeMap;

Graph(Map<String, Node> nodesMap) {
this.nodeMap = nodesMap;
}

/**
* thread unsafe
* Before prepare, the Graph only has vector from child to super.
* this method will add vector from super to child.
* After prepare, there is a full graph.
*/
void prepare() {
nodeMap.values()
.forEach(n -> {
if (n.parent != null) {
ClassNode parent = n.parent;
if (parent.children == Collections.EMPTY_LIST) {

// optimize for Object
if (parent.entity.name.equals("java/lang/Object")) {
parent.children = new ArrayList<>(nodeMap.size() >> 1);
} else {
parent.children = new ArrayList<>();
}
}
// all interfaces extends java.lang.Object
// make java.lang.Object subclasses purely
if (n instanceof ClassNode) {
parent.children.add((ClassNode) n);
}
}
n.interfaces.forEach(i -> {
if (n instanceof InterfaceNode) {
if (i.children == Collections.EMPTY_LIST) {
i.children = new ArrayList<>();
}
i.children.add((InterfaceNode) n);
} else {
if (i.implementedClasses == Collections.EMPTY_LIST) {
i.implementedClasses = new ArrayList<>();
}
//noinspection ConstantConditions
i.implementedClasses.add((ClassNode) n);
}
});
});
}

/**
* clear graph info
* called by internal,please do not call.
*/
public void clear() {
nodeMap.clear();
}

public boolean inherit(String child, String parent) {
Node childNode = nodeMap.get(child);
Node parentNode = nodeMap.get(parent);
Expand Down Expand Up @@ -301,7 +250,7 @@ public MethodEntity confirmOriginMethod(String owner, String name, String desc)
return node.confirmOriginMethod(name, desc);
}

public Map<String, Node> getNodes(){
public Map<String, Node> getNodes() {
return Collections.unmodifiableMap(nodeMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@
import org.objectweb.asm.Opcodes;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class GraphBuilder {
// Key is class name. value is class node.
private Map<String, Node> nodeMap = new ConcurrentHashMap<>(2 >> 16);
protected Map<String, Node> nodeMap = new ConcurrentHashMap<>(2 >> 16);
private volatile Graph graph;

//useless
public GraphBuilder(TransformContext context) {

}
Expand All @@ -36,7 +38,7 @@ public void add(ClassEntity entity) {

// thread safe
public void add(ClassEntity entity, boolean fromCache) {
Node current = getOrPutEmpty((entity.access & Opcodes.ACC_INTERFACE) != 0, entity.name);
final Node current = getOrPutEmpty((entity.access & Opcodes.ACC_INTERFACE) != 0, entity.name);
if (!current.defined.compareAndSet(false, true)) {
if (fromCache) {
//先正式添加后面再添加cache,防止cache覆盖了新的数据,此处return
Expand All @@ -58,6 +60,16 @@ public void add(ClassEntity entity, boolean fromCache) {
Node node = getOrPutEmpty(false, entity.superName);
if (node instanceof ClassNode) {
superNode = (ClassNode) node;
// all interfaces extends java.lang.Object
// make java.lang.Object subclasses purely
if (current instanceof ClassNode) {
synchronized (superNode) {
if (superNode.children == Collections.EMPTY_LIST) {
superNode.children = new LinkedList<>();
}
superNode.children.add((ClassNode) current);
}
}
} else {
throw new RuntimeException(String.format("%s is not a class. Maybe there are duplicate class files in the project.", entity.superName));
}
Expand All @@ -67,6 +79,20 @@ public void add(ClassEntity entity, boolean fromCache) {
.map(i -> {
Node node = getOrPutEmpty(true, i);
if (node instanceof InterfaceNode) {
final InterfaceNode interfaceNode = (InterfaceNode) node;
synchronized (interfaceNode) {
if (current instanceof InterfaceNode) {
if (interfaceNode.children == Collections.EMPTY_LIST) {
interfaceNode.children = new LinkedList<>();
}
interfaceNode.children.add((InterfaceNode) current);
} else if (current instanceof ClassNode) {
if (interfaceNode.implementedClasses == Collections.EMPTY_LIST) {
interfaceNode.implementedClasses = new LinkedList<>();
}
interfaceNode.implementedClasses.add((ClassNode) current);
}
}
return (InterfaceNode) node;
} else {
throw new RuntimeException(String.format("%s is not a interface. Maybe there are duplicate class files in the project.", i));
Expand All @@ -91,8 +117,7 @@ public Graph build() {
if (graph == null) {
synchronized (this) {
if (graph == null) {
graph = new Graph(nodeMap);
graph.prepare();
graph = new EditableGraph(nodeMap);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package com.ss.android.ugc.bytex.common.graph.cache

import com.ss.android.ugc.bytex.common.graph.Graph
import com.ss.android.ugc.bytex.common.graph.Node
import com.ss.android.ugc.bytex.transformer.concurrent.Schedulers
import java.util.concurrent.Future

/**
* Created by yangzhiqian on 2020/7/12<br/>
*/
class AsynchronousGraphCache<T>(realCache: IGraphCache<T>) : IGraphCache<T> by realCache {
internal class AsynchronousGraphCacheStorage<T>(realCacheStorage: GraphCacheStorage<T>) : GraphCacheStorage<T> by realCacheStorage {

fun loadCacheAsync(t: T?, graphBuilder: CachedGraphBuilder): Future<Boolean> {
return Schedulers.IO().submit { loadCache(t, graphBuilder) }
}

fun saveCacheAsync(t: T?, graph: Graph): Future<Boolean> {
return Schedulers.IO().submit { saveCache(t, graph) }
fun saveCacheAsync(t: T?, nodes: Map<String, Node>): Future<Boolean> {
return Schedulers.IO().submit { saveCache(t, nodes) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.ss.android.ugc.bytex.common.graph.cache;
/**
* Created by yangzhiqian on 2020-7-13<br/>
*/
interface ICache<T, D> {
internal interface CacheStorage<T, D> {

fun loadCache(t: T?): D?

Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,122 @@
package com.ss.android.ugc.bytex.common.graph.cache;

import com.ss.android.ugc.bytex.common.configuration.BooleanProperty
import com.ss.android.ugc.bytex.common.graph.Graph
import com.ss.android.ugc.bytex.common.graph.GraphBuilder
import com.ss.android.ugc.bytex.common.graph.*
import java.io.File
import java.util.concurrent.Future
import java.util.*
import java.util.concurrent.ConcurrentHashMap

class CachedGraphBuilder(private val graphCacheFile: File?, shouldLoadCache: Boolean, private val shouldSaveCache: Boolean) : GraphBuilder() {
private val graphCache =
AsynchronousGraphCache(
DefaultGraphCache(
GsonClassesCache(
if (BooleanProperty.ENABLE_RAM_CACHE.value()) {
RAMClassesCache
AsynchronousGraphCacheStorage(
DefaultGraphCacheStorage(
if (BooleanProperty.ENABLE_RAM_CACHE.value() && BooleanProperty.ENABLE_RAM_NODES_CACHE.value()) {
RamNodeCacheStorage
} else {
null
},
GsonFileClassCacheStorage(
if (BooleanProperty.ENABLE_RAM_CACHE.value() && BooleanProperty.ENABLE_RAM_CLASSES_CACHE.value()) {
RamClassesCacheStorage
} else {
null
}
)
)
)
private var grapCacheFuture: Future<Boolean>? = null
private val isCacheValid =
shouldLoadCache &&
if (BooleanProperty.ENABLE_ASYNC_LOAD_CACHE.value()) {
grapCacheFuture = graphCache.loadCacheAsync(graphCacheFile, this)
true
} else {
graphCache.loadCache(graphCacheFile, this).apply {
graphCacheFile?.delete()
if (graphCacheFile != null) {
RAMClassesCache.clearCache(graphCacheFile)
}
if (!this) {
throw IllegalStateException("Failed to load cache")
}
}
try {
shouldLoadCache && graphCache.loadCache(graphCacheFile, this).apply {
if (!this) {
RamClassesCacheStorage.clear()
RamNodeCacheStorage.clear()
graphCacheFile?.delete()
throw IllegalStateException("Failed to load cache")

}
}
} finally {
if (graphCacheFile != null) {
graphCacheFile.delete()
RamClassesCacheStorage.clearCache(graphCacheFile)
RamNodeCacheStorage.clearCache(graphCacheFile)
}
}


override fun isCacheValid(): Boolean = isCacheValid

@Synchronized
override fun build(): Graph {
//先保证cache执行完成
grapCacheFuture?.get()?.apply {
graphCacheFile?.delete()
if (graphCacheFile != null) {
RAMClassesCache.clearCache(graphCacheFile)
}
if (!this) {
throw IllegalStateException("Failed to load cache")
/**
* from cache during incremental build
*/
internal fun addNodes(nodes: Map<String, Node>) {
//during incremental build, the classes which changed will be added again, and the calculation of the inheritance
//relationship will be repeated. Replace it with a de-duplicated list here
//增量时,原先的class changd会导致被add两次,计算继承关系会重复,这里替换成去重的list
for (node in nodes.values) {
if (node is ClassNode) {
if (node.children.isNotEmpty() && node.children !is SkipDuplicatedList) {
node.children = SkipDuplicatedList(node.children)
}
} else if (node is InterfaceNode) {
if (node.children.isNotEmpty() && node.children !is SkipDuplicatedList) {
node.children = SkipDuplicatedList(node.children)
}
if (node.implementedClasses.isNotEmpty() && node.implementedClasses !is SkipDuplicatedList) {
node.implementedClasses = SkipDuplicatedList(node.implementedClasses)
}
}
}
if (nodeMap.isEmpty() && nodes is ConcurrentHashMap) {
//减少复制
nodeMap = nodes
} else {
nodeMap.putAll(nodes)
}
}

@Synchronized
override fun build(): Graph {
val graph = super.build()
if (shouldSaveCache) {
if (BooleanProperty.ENABLE_ASYNC_SAVE_CACHE.value()) {
graphCache.saveCacheAsync(graphCacheFile, graph)
graphCache.saveCacheAsync(graphCacheFile, nodeMap)
} else {
graphCache.saveCache(graphCacheFile, graph)
graphCache.saveCache(graphCacheFile, nodeMap)
}
}
return graph
}


internal class SkipDuplicatedList<T>(c: Collection<T>) : LinkedList<T>(c) {

override fun add(element: T): Boolean {
if (contains(element)) {
return false
}
return super.add(element)
}

override fun add(index: Int, element: T) {
if (contains(element)) {
return
}
super.add(index, element)
}

override fun addFirst(element: T) {
if (contains(element)) {
return
}
super.addFirst(element)
}

override fun addLast(element: T) {
if (contains(element)) {
return
}
super.addLast(element)
}
}
}
Loading

0 comments on commit 749d951

Please sign in to comment.