diff --git a/picocsv/build.gradle.kts b/picocsv/build.gradle.kts new file mode 100644 index 0000000..c42a2bf --- /dev/null +++ b/picocsv/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + jmh(rootProject) + implementation("com.github.nbbrd.picocsv:picocsv:2.4.0") +} diff --git a/picocsv/src/jmh/java/picocsv/PicocsvBenchmark.java b/picocsv/src/jmh/java/picocsv/PicocsvBenchmark.java new file mode 100644 index 0000000..09a3c38 --- /dev/null +++ b/picocsv/src/jmh/java/picocsv/PicocsvBenchmark.java @@ -0,0 +1,66 @@ +package picocsv; + +import java.io.IOException; +import java.util.Collection; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.infra.Blackhole; + +import de.siegmar.csvbenchmark.CsvConstants; +import de.siegmar.csvbenchmark.ICsvReader; +import de.siegmar.csvbenchmark.ICsvWriter; +import de.siegmar.csvbenchmark.util.NullWriter; +import de.siegmar.csvbenchmark.util.RowSupplier; + +public class PicocsvBenchmark { + + @State(Scope.Benchmark) + public static class WriteState { + + private final RowSupplier rowSupplier = new RowSupplier(CsvConstants.RECORDS); + private ICsvWriter writer; + + @Setup + public void setup(final Blackhole bh) throws IOException { + writer = Factory.writer(new NullWriter(bh)); + } + + @TearDown + public void teardown() throws IOException { + writer.close(); + } + + } + + @Benchmark + public void write(final WriteState state) throws Exception { + state.writer.writeRecord(state.rowSupplier.get()); + } + + @State(Scope.Benchmark) + public static class ReadState { + + private ICsvReader reader; + + @Setup + public void setup() throws IOException { + reader = Factory.reader(); + } + + @TearDown + public void teardown() throws IOException { + reader.close(); + } + + } + + @Benchmark + public Collection read(final ReadState state) throws Exception { + return state.reader.readRecord(); + } + +} diff --git a/picocsv/src/main/java/picocsv/Factory.java b/picocsv/src/main/java/picocsv/Factory.java new file mode 100644 index 0000000..03a731b --- /dev/null +++ b/picocsv/src/main/java/picocsv/Factory.java @@ -0,0 +1,76 @@ +package picocsv; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; + +import de.siegmar.csvbenchmark.CsvConstants; +import de.siegmar.csvbenchmark.ICsvReader; +import de.siegmar.csvbenchmark.ICsvWriter; +import de.siegmar.csvbenchmark.util.InfiniteDataReader; +import nbbrd.picocsv.Csv; + +public final class Factory { + + private static final Csv.Format FORMAT = Csv.Format.DEFAULT + .toBuilder() + .delimiter(CsvConstants.SEPARATOR) + .separator(Csv.Format.UNIX_SEPARATOR) + .quote(CsvConstants.DELIMITER) + .build(); + + // Read performances seems highly related to the buffer size + // Slower: the default buf size (8192) is too big for the benchmark data + private static final int BUF_SIZE_SLOWER = Csv.DEFAULT_CHAR_BUFFER_SIZE; + // Equivalent: a random low buf size seems efficient + private static final int BUF_SIZE_EQUIVALENT = 200; + // Faster: a perfect buf size (163) aligns the stars and performs very well + private static final int BUF_SIZE_FASTER = CsvConstants.DATA.length(); + + private Factory() { + } + + public static ICsvReader reader() throws IOException { + return new ICsvReader() { + private final Csv.Reader csvReader = Csv.Reader.of(FORMAT, Csv.ReaderOptions.DEFAULT, + new InfiniteDataReader(CsvConstants.DATA), BUF_SIZE_SLOWER); + + @Override + public List readRecord() throws IOException { + final List result = new ArrayList<>(); + if (csvReader.readLine()) { + while (csvReader.readField()) { + result.add(csvReader.toString()); + } + } + return result; + } + + @Override + public void close() throws IOException { + csvReader.close(); + } + }; + } + + public static ICsvWriter writer(final Writer writer) throws IOException { + return new ICsvWriter() { + private final Csv.Writer csvWriter = Csv.Writer.of(FORMAT, Csv.WriterOptions.DEFAULT, + writer, Csv.DEFAULT_CHAR_BUFFER_SIZE); + + @Override + public void writeRecord(final List fields) throws IOException { + for (final String field : fields) { + csvWriter.writeField(field); + } + csvWriter.writeEndOfLine(); + } + + @Override + public void close() throws IOException { + csvWriter.close(); + } + }; + } +} diff --git a/picocsv/src/test/java/picocsv/FormatTest.java b/picocsv/src/test/java/picocsv/FormatTest.java new file mode 100644 index 0000000..3ea11f1 --- /dev/null +++ b/picocsv/src/test/java/picocsv/FormatTest.java @@ -0,0 +1,37 @@ +package picocsv; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.StringWriter; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import de.siegmar.csvbenchmark.CsvConstants; +import de.siegmar.csvbenchmark.ICsvReader; +import de.siegmar.csvbenchmark.ICsvWriter; + +public class FormatTest { + + @Test + public void reader() throws Exception { + try (ICsvReader reader = Factory.reader()) { + for (final List row : CsvConstants.RECORDS) { + assertEquals(row, reader.readRecord()); + } + } + } + + @Test + public void writer() throws Exception { + final StringWriter sw = new StringWriter(); + try (ICsvWriter writer = Factory.writer(sw)) { + for (final List row : CsvConstants.RECORDS) { + writer.writeRecord(row); + } + } + + assertEquals(CsvConstants.DATA, sw.toString()); + } + +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 93c6f51..3e3282d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,6 +5,7 @@ include("fastcsv") include("jackson") include("javacsv") include("opencsv") +include("picocsv") include("simpleflatmapper") include("supercsv") include("univocity")