Add theoretical performance optimization for Scanner
Useless for now until I find a way of running transformers before mods load
This commit is contained in:
parent
8cdc425d7d
commit
ad5fcf44e5
|
|
@ -27,6 +27,7 @@ import org.embeddedt.modernfix.ModernFix;
|
|||
import org.embeddedt.modernfix.classloading.api.IHashableTransformer;
|
||||
import org.embeddedt.modernfix.classloading.hashers.CoreModTransformerHasher;
|
||||
import org.embeddedt.modernfix.classloading.hashers.MixinTransformerHasher;
|
||||
import org.embeddedt.modernfix.util.FileUtil;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.spongepowered.asm.launch.MixinLaunchPluginLegacy;
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ import javax.lang.model.SourceVersion;
|
|||
public class ModernFixCachingClassTransformer extends ClassTransformer {
|
||||
private static final Logger LOGGER = LogManager.getLogger("ModernFixCachingTransformer");
|
||||
|
||||
private final File CLASS_CACHE_FOLDER = childFile(FMLPaths.GAMEDIR.get().resolve("modernfix").resolve("classCacheV1").toFile());
|
||||
private final File CLASS_CACHE_FOLDER = FileUtil.childFile(FMLPaths.GAMEDIR.get().resolve("modernfix").resolve("classCacheV1").toFile());
|
||||
private final LaunchPluginHandler pluginHandler;
|
||||
private final Map<String, ILaunchPluginService> plugins;
|
||||
private final TransformStore transformStore;
|
||||
|
|
@ -56,11 +57,6 @@ public class ModernFixCachingClassTransformer extends ClassTransformer {
|
|||
}
|
||||
});
|
||||
|
||||
private static File childFile(File file) {
|
||||
file.getParentFile().mkdirs();
|
||||
return file;
|
||||
}
|
||||
|
||||
public ModernFixCachingClassTransformer(TransformStore transformStore, LaunchPluginHandler pluginHandler, TransformingClassLoader transformingClassLoader, TransformerAuditTrail trail) {
|
||||
super(transformStore, pluginHandler, transformingClassLoader, trail);
|
||||
this.transformStore = transformStore;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ public class ModernFixEarlyConfig {
|
|||
this.addMixinRule("perf.thread_priorities", true);
|
||||
this.addMixinRule("perf.preload_block_classes", false);
|
||||
this.addMixinRule("perf.sync_executor_sleep", true);
|
||||
this.addMixinRule("perf.scan_cache", true);
|
||||
this.addMixinRule("perf.parallel_blockstate_cache_rebuild", true);
|
||||
this.addMixinRule("perf.deduplicate_location", true);
|
||||
this.addMixinRule("safety", true);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.scan_cache;
|
||||
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.Scanner;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
import org.embeddedt.modernfix.scanning.CachedScanner;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Scanner.class)
|
||||
public class ScannerMixin {
|
||||
@Shadow @Final private ModFile fileToScan;
|
||||
|
||||
@Inject(method = "scan", at = @At(value = "HEAD"), cancellable = true, remap = false)
|
||||
private void useCachedScanResults(CallbackInfoReturnable<ModFileScanData> cir) {
|
||||
ModFileScanData cached = CachedScanner.getCachedDataForFile(this.fileToScan);
|
||||
if(cached != null)
|
||||
cir.setReturnValue(cached);
|
||||
}
|
||||
|
||||
@Inject(method = "scan", at = @At(value = "TAIL"), remap = false)
|
||||
private void saveCachedScanResults(CallbackInfoReturnable<ModFileScanData> cir) {
|
||||
CachedScanner.saveCachedDataForFile(this.fileToScan, cir.getReturnValue());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
package org.embeddedt.modernfix.scanning;
|
||||
|
||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||
import net.minecraftforge.fml.loading.FMLPaths;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
import net.minecraftforge.forgespi.language.IModLanguageProvider;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CachedScanner {
|
||||
private static final Path SCAN_CACHE_FOLDER = FMLPaths.GAMEDIR.get().resolve("modernfix").resolve("scanCacheV1");
|
||||
|
||||
private static File getCacheFileLocation(ModFile file) {
|
||||
Path modPath = FMLPaths.MODSDIR.get().relativize(file.getFilePath());
|
||||
return SCAN_CACHE_FOLDER.resolve(modPath).toFile();
|
||||
}
|
||||
|
||||
private static MessageDigest modFileDigest = LamdbaExceptionUtils.uncheck(() -> MessageDigest.getInstance("SHA-256"));
|
||||
|
||||
private static byte[] computeModFileHash(ModFile file) {
|
||||
modFileDigest.reset();
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
try(BufferedInputStream stream = new BufferedInputStream(Files.newInputStream(file.getFilePath()))) {
|
||||
while((bytesRead = stream.read(buffer, 0, buffer.length)) > 0) {
|
||||
modFileDigest.update(buffer, 0, bytesRead);
|
||||
}
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException("Failed to read mod file", e);
|
||||
}
|
||||
return modFileDigest.digest();
|
||||
}
|
||||
|
||||
private static String getCurrentLangVersion(ModFile file) {
|
||||
IModLanguageProvider loader = file.getLoader();
|
||||
String currentLangVersion = loader.getClass().getPackage().getImplementationVersion();
|
||||
if(currentLangVersion == null)
|
||||
currentLangVersion = "[none]";
|
||||
return currentLangVersion;
|
||||
}
|
||||
|
||||
static class SerializedClassData implements Serializable {
|
||||
public String classTypeDesc;
|
||||
public String parentTypeDesc;
|
||||
public ArrayList<String> interfacesTypeDesc;
|
||||
}
|
||||
|
||||
static class SerializedAnnotationData implements Serializable {
|
||||
public String annotationTypeDesc;
|
||||
public ElementType targetTypeDesc;
|
||||
public String classTypeDesc;
|
||||
public String memberName;
|
||||
public Map<String, Object> annotationData;
|
||||
}
|
||||
|
||||
private static ModFileScanData deserializeScanData(ModFile file, ObjectInputStream stream) throws IOException, ClassNotFoundException {
|
||||
ModFileScanData result = new ModFileScanData();
|
||||
result.addModFileInfo(file.getModFileInfo());
|
||||
/* Read all the classes */
|
||||
ArrayList<SerializedClassData> classDataList = (ArrayList<SerializedClassData>)stream.readObject();
|
||||
Set<ModFileScanData.ClassData> classDataSet = result.getClasses();
|
||||
for(SerializedClassData data : classDataList) {
|
||||
classDataSet.add(new ModFileScanData.ClassData(
|
||||
Type.getType(data.classTypeDesc),
|
||||
Type.getType(data.parentTypeDesc),
|
||||
data.interfacesTypeDesc.stream().map(Type::getType).collect(Collectors.toSet())));
|
||||
}
|
||||
/* Read all the annotations */
|
||||
ArrayList<SerializedAnnotationData> annotationDataList = (ArrayList<SerializedAnnotationData>)stream.readObject();
|
||||
Set<ModFileScanData.AnnotationData> annotationDataSet = result.getAnnotations();
|
||||
for(SerializedAnnotationData data : annotationDataList) {
|
||||
annotationDataSet.add(new ModFileScanData.AnnotationData(
|
||||
Type.getType(data.annotationTypeDesc),
|
||||
data.targetTypeDesc,
|
||||
Type.getType(data.classTypeDesc),
|
||||
data.memberName,
|
||||
data.annotationData
|
||||
));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ModFileScanData getCachedDataForFile(ModFile file) {
|
||||
byte[] currentHash = computeModFileHash(file);
|
||||
String currentLangVersion = getCurrentLangVersion(file);
|
||||
try(ObjectInputStream stream = new ObjectInputStream(new FileInputStream(getCacheFileLocation(file)))) {
|
||||
byte[] modFileHash = (byte[])stream.readObject();
|
||||
if(!Arrays.equals(modFileHash, currentHash)) {
|
||||
return null;
|
||||
}
|
||||
String langVersion = stream.readUTF();
|
||||
if(!langVersion.equals(currentLangVersion))
|
||||
return null;
|
||||
return deserializeScanData(file, stream);
|
||||
} catch(IOException | ClassNotFoundException e) {
|
||||
if(!(e instanceof FileNotFoundException))
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Field classDataTypeField, classDataParentField, classDataInterfacesField;
|
||||
|
||||
public static void saveCachedDataForFile(ModFile file, ModFileScanData scanData) {
|
||||
try(ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(getCacheFileLocation(file)))) {
|
||||
stream.writeObject(computeModFileHash(file));
|
||||
stream.writeObject(getCurrentLangVersion(file));
|
||||
/* serialize scan data */
|
||||
ArrayList<SerializedClassData> serializedClassDataList = new ArrayList<>();
|
||||
for(ModFileScanData.ClassData data : scanData.getClasses()) {
|
||||
SerializedClassData sData = new SerializedClassData();
|
||||
if(classDataTypeField == null) {
|
||||
classDataTypeField = ModFileScanData.ClassData.class.getDeclaredField("clazz");
|
||||
classDataTypeField.setAccessible(true);
|
||||
}
|
||||
sData.classTypeDesc = ((Type)classDataTypeField.get(data)).getDescriptor();
|
||||
if(classDataTypeField == null) {
|
||||
classDataTypeField = ModFileScanData.ClassData.class.getDeclaredField("clazz");
|
||||
classDataTypeField.setAccessible(true);
|
||||
}
|
||||
sData.classTypeDesc = ((Type)classDataTypeField.get(data)).getDescriptor();
|
||||
if(classDataInterfacesField == null) {
|
||||
classDataInterfacesField = ModFileScanData.ClassData.class.getDeclaredField("interfaces");
|
||||
classDataInterfacesField.setAccessible(true);
|
||||
}
|
||||
sData.interfacesTypeDesc = ((Set<Type>)classDataInterfacesField.get(data)).stream().map(Type::getDescriptor).collect(Collectors.toCollection(ArrayList::new));
|
||||
serializedClassDataList.add(sData);
|
||||
}
|
||||
stream.writeObject(serializedClassDataList);
|
||||
ArrayList<SerializedAnnotationData> serializedAnnotationDataList = new ArrayList<>();
|
||||
for(ModFileScanData.AnnotationData data : scanData.getAnnotations()) {
|
||||
SerializedAnnotationData sData = new SerializedAnnotationData();
|
||||
sData.annotationTypeDesc = data.getAnnotationType().getDescriptor();
|
||||
sData.targetTypeDesc = data.getTargetType();
|
||||
sData.classTypeDesc = data.getClassType().getDescriptor();
|
||||
sData.memberName = data.getMemberName();
|
||||
sData.annotationData = data.getAnnotationData();
|
||||
serializedAnnotationDataList.add(sData);
|
||||
; }
|
||||
stream.writeObject(serializedAnnotationDataList);
|
||||
} catch(IOException | ReflectiveOperationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/main/java/org/embeddedt/modernfix/util/FileUtil.java
Normal file
10
src/main/java/org/embeddedt/modernfix/util/FileUtil.java
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class FileUtil {
|
||||
public static File childFile(File file) {
|
||||
file.getParentFile().mkdirs();
|
||||
return file;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user