JMHProcessor.kt 4.65 KB
Newer Older
1
2
3
4
package se.lth.cs

import org.apache.commons.csv.CSVFormat
import org.apache.commons.csv.CSVParser
5
import org.apache.commons.csv.CSVPrinter
6
import org.apache.commons.csv.CSVRecord
7
import java.io.*
8
import java.lang.Exception
9
10

class JMHProcessor {
11

12
    fun process(file: File): List<JMHRecord> {
Noric Couderc's avatar
Noric Couderc committed
13
        return process(FileReader(file))
14
15
    }

16
    data class JMHRecord(val seed : Int, val size : Int, val baseStructureSize : Int, val collection : String, val datastructure : String, val best : String) {
Noric Couderc's avatar
Noric Couderc committed
17
        fun toList() : List<String> {
18
            return listOf(collection, seed.toString(), size.toString() , baseStructureSize.toString(), datastructure, best)
Noric Couderc's avatar
Noric Couderc committed
19
20
        }
    }
21

22
23
24
25
26
27
28
    private val selectedColumns = listOf(
            "Benchmark",
            "Param: seed",
            "Param: applicationSize",
            "Param: baseStructureSize"
    )

29
    fun process(reader : Reader): List<JMHRecord> {
30
        var parser = CSVParser(reader, CSVFormat.DEFAULT.withFirstRecordAsHeader())
Noric Couderc's avatar
Noric Couderc committed
31
        // We are grouping the parameters by any parameter except the data structure name (which we want)
32

33
        // We group the records by our selected columns
34
35
36
37
        val seedsToRecords = parser.records.groupBy { record ->
            selectedColumns.map { column -> record.get(column) }
        }

38
        return seedsToRecords.values.flatMap { records ->
Noric Couderc's avatar
Noric Couderc committed
39
            val interfaceName = records[0].get("Benchmark").let { processBenchmarkName(it) }
40
            val seed = records[0].get("Param: seed")
41
                    .let { Integer.parseInt(it)}
42
            val size = records[0].get("Param: applicationSize")
43
                    .let { Integer.parseInt(it)}
44
45
            val baseStructureSize = records[0].get("Param: baseStructureSize")
                    .let { Integer.parseInt(it)}
46
            val best = getBestDataStructure(records)
47
48
49
50
            records.map{

                JMHRecord(seed, size, baseStructureSize, interfaceName, it.get("Param: datastructureName"), best!!)
            }
51
52
        }
    }
53

54
55
56
57
58
59
    private fun getBestDataStructure(records : List<CSVRecord>): String? {
        // Precondition
        // All records must have
        // - same seed
        // - same benchmark (List, Map, etc)
        // - same application size
60
        // - same base structure size
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
        assert(records.all { record ->
            selectedColumns.map{ record.get(it)} ==
            selectedColumns.map{ records[0].get(it)}})

        val recordsByBaseSize = records.groupBy { it.get("Param: baseStructureSize") }

        // Map from benchmark base structure size to record with maximum score
        val bestRecords = recordsByBaseSize
                        .mapValues { (k, v) -> v.maxBy { it.get("Score") }}

        // We count the number of times the data structure has won
        // (Computing a histogram of the data structure names)
        val bestScoreHist =
                bestRecords.values.groupBy { it!!.get("Param: datastructureName") }
                        .mapValues { (k, v) -> v.size}

        return bestScoreHist.maxBy { (k, v) -> v }!!.key
    }

Noric Couderc's avatar
Noric Couderc committed
80
    class JMHProcessorException(override val message: String?) : Exception(message)
81
82
83
84
85
86
87
88
89

    fun processBenchmarkName(benchmark : String) : String {
        val options = listOf("List", "Map", "Set")
        val name = benchmark.findAnyOf(options)?.second
        if (name.isNullOrBlank()) {
            throw JMHProcessorException("Benchmark name does not contain any of $options")
        }
        return name
    }
90
91
92
93

    /**
     * Prints the given records to a file
     */
94
    fun print(writer : Writer, records : List<JMHRecord>) {
95
        val printer = CSVPrinter(writer, CSVFormat.DEFAULT.withFirstRecordAsHeader())
96
        printer.printRecord("Interface", "Seed", "Size", "BaseStructureSize", "Best")
97
        for (record in records) {
Noric Couderc's avatar
Noric Couderc committed
98
            printer.printRecord(record.toList())
99
100
101
        }
        writer.close()
    }
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

    companion object {
        /**
         * Static method giving the expected CSV header
         * @return A string for the expected CSV header
         */
        fun getExpectedHeader() : String {
            val header =
                    listOf(
                            "Benchmark",
                            "Mode",
                            "Threads",
                            "Samples",
                            "Score",
                            "Score Error (99.9%)",
                            "Unit",
                            "Param: applicationSize",
                            "Param: baseStructureSize",
                            "Param: datastructureName",
                            "Param: seed"
                    ).map { "\"$it\""}.joinToString(",")
            return header
        }
    }
126
}