Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ public enum AmortizeMode {
@Config.RequiresMcRestart
public static boolean fastBlockLookup;

@Config.Comment("Speedup language file loading")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public static boolean fastLangLoad;

@Config.Comment("Optimize mob spawning")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,11 @@ public enum Mixins implements IMixins {
.addCommonMixins("minecraft.fastload.MixinBlock_FastLookup")
.setApplyIf(() -> SpeedupsConfig.fastBlockLookup)
.setPhase(Phase.EARLY)),
FAST_LOCALE_LOAD(new MixinBuilder("Improve speed of language file loading")
.addClientMixins("minecraft.fastload.MixinLocale")
.addCommonMixins("minecraft.fastload.MixinStringTranslate")
.setApplyIf(() -> SpeedupsConfig.fastLangLoad)
.setPhase(Phase.EARLY)),
SPEEDUP_LEAF_DECAY(new MixinBuilder()
.addCommonMixins("minecraft.MixinBlockLeaves_BFSDecay")
.setApplyIf(() -> SpeedupsConfig.speedupLeafDecay)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload;

import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import net.minecraft.client.resources.Locale;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import com.google.common.base.Splitter;
import com.llamalad7.mixinextras.sugar.Local;
import com.mitchej123.hodgepodge.util.OffThreadLineIterator;

@Mixin(Locale.class)
public class MixinLocale {

@Redirect(
method = "loadLocaleData(Ljava/io/InputStream;)V",
at = @At(
value = "INVOKE",
target = "Lorg/apache/commons/io/IOUtils;readLines(Ljava/io/InputStream;Ljava/nio/charset/Charset;)Ljava/util/List;",
remap = false))
private List<String> readNoLines(InputStream input, Charset encoding) {
return Collections.emptyList();
}

@Redirect(
method = "loadLocaleData(Ljava/io/InputStream;)V",
at = @At(value = "INVOKE", target = "Ljava/util/List;iterator()Ljava/util/Iterator;", remap = false))
private Iterator<String> createOffThreadIterator(List<String> instance, @Local(argsOnly = true) InputStream input) {
return new OffThreadLineIterator(input);
}

@Redirect(
method = "loadLocaleData(Ljava/io/InputStream;)V",
at = @At(
value = "INVOKE",
target = "Lcom/google/common/base/Splitter;split(Ljava/lang/CharSequence;)Ljava/lang/Iterable;",
remap = false))
private Iterable<String> dontSplit(Splitter instance, CharSequence sequence) {
return null;
}

@Redirect(
method = "loadLocaleData(Ljava/io/InputStream;)V",
at = @At(
value = "INVOKE",
target = "Lcom/google/common/collect/Iterables;toArray(Ljava/lang/Iterable;Ljava/lang/Class;)[Ljava/lang/Object;",
remap = false))
private Object[] split(Iterable<String> iterable, Class<String> type, @Local String s) {
for (int i = 0; i < s.length() - 1; i++) {
if (s.charAt(i) == '=') {
return new String[] { s.substring(0, i), s.substring(i + 1) };
}
}
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload;

import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import net.minecraft.util.StringTranslate;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import com.google.common.base.Splitter;
import com.llamalad7.mixinextras.sugar.Local;
import com.mitchej123.hodgepodge.util.OffThreadLineIterator;

@Mixin(value = StringTranslate.class, remap = false)
public class MixinStringTranslate {

@Redirect(
method = "parseLangFile",
at = @At(
value = "INVOKE",
target = "Lorg/apache/commons/io/IOUtils;readLines(Ljava/io/InputStream;Ljava/nio/charset/Charset;)Ljava/util/List;",
remap = false))
private static List<String> readNoLines(InputStream input, Charset encoding) {
return Collections.emptyList();
}

@Redirect(
method = "parseLangFile",
at = @At(value = "INVOKE", target = "Ljava/util/List;iterator()Ljava/util/Iterator;", remap = false))
private static Iterator<String> createOffThreadIterator(List<String> instance,
@Local(argsOnly = true) InputStream input) {
return new OffThreadLineIterator(input);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait a moment, how does this work? Instead of performing blocking IO operations on the main thread, we create one new off-thread and block the main thread while the off-thread is performing IO operations?

The only possible benefit I see from this is being able to process the lines while the other thread is waiting on IO, but that's a pretty minor benefit assuming this process is IO-bound.

}

@Redirect(
method = "parseLangFile",
at = @At(
value = "INVOKE",
target = "Lcom/google/common/base/Splitter;split(Ljava/lang/CharSequence;)Ljava/lang/Iterable;",
remap = false))
private static Iterable<String> dontSplit(Splitter instance, CharSequence sequence) {
return null;
}

@Redirect(
method = "parseLangFile",
at = @At(
value = "INVOKE",
target = "Lcom/google/common/collect/Iterables;toArray(Ljava/lang/Iterable;Ljava/lang/Class;)[Ljava/lang/Object;",
remap = false))
private static Object[] split(Iterable<String> iterable, Class<String> type, @Local String s) {
for (int i = 0; i < s.length() - 1; i++) {
if (s.charAt(i) == '=') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use indexOf?

return new String[] { s.substring(0, i), s.substring(i + 1) };
}
}
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.mitchej123.hodgepodge.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.commons.io.Charsets;

import com.mitchej123.hodgepodge.Common;

/**
* Iterator which reads lines of an InputStream is a different thread so that the stream can continue to be read while
* earlier lines are being processed.
*/
public class OffThreadLineIterator implements Iterator<String> {

private static final ExecutorService executor = Executors
.newSingleThreadExecutor(OffThreadLineIterator::createThread);;

private final BufferedReader reader;
private final LinkedBlockingQueue<String> pendingLines = new LinkedBlockingQueue<>();
private volatile boolean finished;
private String next;

public OffThreadLineIterator(InputStream stream) {
reader = new BufferedReader(new InputStreamReader(stream, Charsets.toCharset(Charsets.UTF_8)));
executor.submit(this::run);
}

@Override
public boolean hasNext() {
try {
next = pendingLines.take();
} catch (InterruptedException e) {
return false;
}
return !finished || !pendingLines.isEmpty();
}

@Override
public String next() {
return next;
}

private void run() {
try {
String line = reader.readLine();
while (line != null) {
pendingLines.add(line);
line = reader.readLine();
}
} catch (Exception ex) {
Common.log.error("Error reading line", ex);
}
finished = true;
pendingLines.add("");
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static Thread createThread(Runnable r) {
Thread thread = new Thread(r, "OffThreadLineIterator");
thread.setDaemon(true);
return thread;
}
}