Loading...
tests/MallocBenchTest/MALLOC_BENCH/run-malloc-benchmarks /dev/null libmalloc-409.81.2
--- /dev/null
+++ libmalloc/libmalloc-409.81.2/tests/MallocBenchTest/MALLOC_BENCH/run-malloc-benchmarks
@@ -0,0 +1,396 @@
+#!/usr/bin/env ruby
+# coding: utf-8
+
+require 'getoptlong'
+require 'pathname'
+
+$benchmarks_all = [
+    # Single-threaded benchmarks.
+    "churn",
+    "list_allocate",
+    "tree_allocate",
+    "tree_churn",
+    "fragment",
+    "fragment_iterate",
+    "medium",
+    "big",
+
+    # Benchmarks based on browser recordings.
+    "facebook",
+    "reddit",
+    "flickr",
+    "theverge",
+    "nimlang",
+
+    # Multi-threaded benchmark variants.
+    "message_one",
+    "message_many",
+    "churn --parallel",
+    "list_allocate --parallel",
+    "tree_allocate --parallel",
+    "tree_churn --parallel",
+    "fragment --parallel",
+    "fragment_iterate --parallel",
+
+    # These tests often crash TCMalloc: <rdar://problem/13657137>.
+    "medium --parallel",
+    "big --parallel",
+
+    # Enable these tests to test memory footprint. The way they run is not
+    # really compatible with throughput testing.
+    # "reddit_memory_warning --runs 0",
+    # "flickr_memory_warning --runs 0",
+    # "theverge_memory_warning --runs 0",
+
+    # Enable this test to test shrinking back down from a large heap while a process remains active.
+    # The way it runs is not really compatible with throughput testing.
+    # "balloon"
+    "facebook --parallel",
+    "reddit --parallel",
+    "flickr --parallel",
+    "theverge --parallel",
+    # "nimlang --use-thread-id",
+]
+
+$benchmarks_memory = [
+    "facebook",
+    "reddit",
+    "flickr",
+    "theverge",
+    "nimlang"
+]
+
+$benchmarks_memory_warning = [
+    "reddit_memory_warning --runs 0",
+    "flickr_memory_warning --runs 0",
+    "theverge_memory_warning --runs 0",
+]
+
+$benchmarks = $benchmarks_all
+$heap = 0
+
+def usage
+	puts "run-malloc-benchmarks [options] /path/to/MallocBench Name:/path/to/libmbmalloc.dylib [ Name:/path/to/libmbmalloc.dylib ]"
+	puts
+	puts "    Runs a suite of memory allocation and access benchmarks."
+    puts
+    puts "    <Name:/path/to/libmbmalloc.dylib> is a symbolic name followed by a path to libmbmalloc.dylib."
+    puts
+    puts "    Specify \"SystemMalloc\" to test the built-in libc malloc."
+    puts "    Specify \"NanoMalloc\" to test the built-in libc malloc using the NanoMalloc zone."
+    puts
+    puts "    Example usage:"
+    puts
+    puts "        run-malloc-benchmarks /BUILD/MallocBench SystemMalloc:/BUILD/libmbmalloc.dylib NanoMalloc:/BUILD/libmbmalloc.dylib"
+    puts "        run-malloc-benchmarks /BUILD/MallocBench FastMalloc:/BUILD/FastMalloc/libmbmalloc.dylib"
+    puts "        run-malloc-benchmarks --benchmark churn SystemMalloc:/BUILD/libmbmalloc.dylib FastMalloc:/BUILD/FastMalloc/libmbmalloc.dylib"
+    puts
+	puts "Options:"
+    puts
+    puts "    --benchmark <benchmark>      Select a single benchmark to run instead of the full suite."
+    puts "    --heap <heap>                Set a baseline heap size."
+    puts
+end
+
+class Dylib
+    attr_reader :name
+    attr_reader :path
+
+    def initialize(name, path)
+        @name = name
+        @path = path
+    end
+end
+
+class Results
+    attr_reader :executionTime
+    attr_reader :peakMemory
+    attr_reader :memoryAtEnd
+
+    def initialize(executionTime, peakMemory, memoryAtEnd)
+        @executionTime = executionTime
+        @peakMemory = peakMemory
+        @memoryAtEnd = memoryAtEnd
+    end
+end
+
+class Stat
+    attr_reader :benchmark
+    attr_reader :result
+
+    def initialize(benchmark, result)
+        @benchmark = benchmark
+        @result = result[/\d+/].to_i
+    end
+end
+
+class TimeStat < Stat
+    def to_s
+    	@result + "ms"
+    end
+end
+
+class MemoryStat < Stat
+    def to_s
+    	@result + "kB"
+    end
+end
+
+class PeakMemoryStat < Stat
+    def to_s
+    	@result + "kB"
+    end
+end
+
+def lpad(str, chars)
+    if str.length > chars
+        str
+    else
+        "%#{chars}s"%(str)
+    end
+end
+
+def rpad(str, chars)
+    while str.length < chars
+        str += " "
+    end
+    str
+end
+
+def computeArithmeticMean(array)
+  sum = 0.0
+  array.each {
+    | value |
+    sum += value
+  }
+  (sum / array.length)
+end
+
+def computeGeometricMean(array)
+  mult = 1.0
+  array.each {
+    | value |
+    mult *= value ? value : 1.0
+  }
+  (mult ** (1.0 / array.length))
+end
+
+def computeHarmonicMean(array)
+  1.0 / computeArithmeticMean(array.collect{ | value | 1.0 / value })
+end
+
+def lowerIsBetter(a, b, better, worse)
+    if b < a
+        return "^ " + (a.to_f / b.to_f).round(2).to_s + "x " + better
+    end
+
+    if b == a
+        return ""
+    end
+
+    "! " + (b.to_f / a.to_f).round(2).to_s + "x " + worse
+end
+
+
+def lowerIsFaster(a, b)
+    lowerIsBetter(a, b, "faster", "slower")
+end
+
+def lowerIsSmaller(a, b)
+    lowerIsBetter(a, b, "smaller", "bigger")
+end
+
+def numberWithDelimiter(number)
+    number.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse
+end
+
+def prettify(number, suffix)
+    numberWithDelimiter(number) + suffix
+end
+
+def parseOptions
+    GetoptLong.new(
+        ['--benchmark', GetoptLong::REQUIRED_ARGUMENT],
+        ['--memory', GetoptLong::NO_ARGUMENT],
+        ['--memory_warning', GetoptLong::NO_ARGUMENT],
+        ['--heap', GetoptLong::REQUIRED_ARGUMENT],
+        ['--help', GetoptLong::NO_ARGUMENT],
+    ).each {
+        | opt, arg |
+        case opt
+        when '--benchmark'
+            $benchmarks = [ arg ]
+        when '--memory'
+            $benchmarks = $benchmarks_memory
+        when '--memory_warning'
+            $benchmarks = $benchmarks_memory_warning
+        when '--heap'
+            $heap = arg
+        when '--help'
+            usage
+            exit 1
+        else
+          raise "bad option: #{opt}"
+        end
+    }
+
+    if ARGV.length < 1
+        puts "Error: No MallocBench specified."
+        exit 1
+    end
+
+    if ARGV.length < 2
+        puts "Error: No dylib specified."
+        exit 1
+    end
+
+    $mallocBench = File.absolute_path(ARGV.shift)
+    if !File.exists?($mallocBench)
+        puts "File not found: #{$mallocBench}."
+        exit 1
+    end
+
+    $buildDir = Pathname.new($mallocBench).dirname
+
+    dylibs = []
+    ARGV.each {
+        | arg |
+        name, path = arg.split(":")
+        if !name || name.length < 1 ||
+            !path || path.length < 1
+            puts "Invalid <Name:/path/to/dylib>: '#{arg}'."
+            exit 1
+        end
+
+        dylib = Dylib.new(name, File.expand_path(path))
+
+        if !File.exists?(dylib.path)
+            puts "File not found: #{dylib.path}."
+            exit 1
+        end
+
+        dylibs.push(dylib)
+    }
+    dylibs
+end
+
+def runBenchmarks(dylibs)
+    executionTime = []
+    peakMemory = []
+    memoryAtEnd = []
+
+    $benchmarks.each {
+        | benchmark |
+
+        executionTime.push([])
+        peakMemory.push([])
+        memoryAtEnd.push([])
+
+        dylibs.each {
+            | dylib |
+
+            $stderr.print "\rRUNNING #{dylib.name}: #{benchmark}...                                "
+            env = "DYLD_LIBRARY_PATH='#{Pathname.new(dylib.path).dirname}' "
+            env += "LD_LIBRARY_PATH='#{Pathname.new(dylib.path).dirname}' "
+            if dylib.name == "NanoMalloc"
+                env += "MallocNanoZone=1 "
+            elsif dylib.name == "SystemMalloc"
+                env += "MallocNanoZone=0 "
+            end
+            input = "cd '#{$buildDir}'; #{env} '#{$mallocBench}' --benchmark #{benchmark} --heap #{$heap}}"
+            output =`#{input}`
+            splitOutput = output.split("\n")
+
+            executionTime[-1].push(TimeStat.new(benchmark, splitOutput[1]))
+            peakMemory[-1].push(PeakMemoryStat.new(benchmark, splitOutput.length > 3 ? splitOutput[2] : "0"))
+            memoryAtEnd[-1].push(MemoryStat.new(benchmark, splitOutput.length > 2 ? splitOutput[3] : "0"))
+        }
+    }
+    $stderr.print "\r                                                                                \n"
+
+    Results.new(executionTime, peakMemory, memoryAtEnd)
+end
+
+def printResults(dylibs, results)
+    def printHeader(dylibs, fieldSize)
+        print
+        print lpad("", fieldSize)
+        print lpad(dylibs[0].name, fieldSize)
+        if dylibs.length > 1
+            print lpad(dylibs[1].name, fieldSize)
+            print lpad("Δ", fieldSize)
+        end
+        print "\n"
+    end
+
+    def printMetric(name, results, compareFunction, suffix, fieldSize)
+        def printMean(name, results, meanFunction, compareFunction, suffix, fieldSize)
+            means = []
+
+            means.push(meanFunction.call(results.collect { | stats | stats[0].result }))
+            print rpad("    " + name, fieldSize)
+            print lpad("#{prettify(means[0].round, suffix)}", fieldSize)
+
+            if results[0][1]
+                means.push(meanFunction.call(results.collect { | stats | stats[1].result }))
+                print lpad("#{prettify(means[1].round, suffix)}", fieldSize)
+                print lpad(compareFunction.call(means[0], means[1]), fieldSize)
+            end
+
+            print "\n"
+        end
+
+        if results[0][0].result == 0
+            return
+        end
+
+        print name + ":\n"
+        results.each {
+            | stats |
+
+            print rpad("    " + stats[0].benchmark, fieldSize)
+            print lpad("#{prettify(stats[0].result, suffix)}", fieldSize)
+
+            if stats[1]
+                print lpad("#{prettify(stats[1].result, suffix)}", fieldSize)
+                print lpad(compareFunction.call(stats[0].result, stats[1].result), fieldSize)
+            end
+
+            print "\n"
+        }
+
+        print "\n"
+
+        printMean("<geometric mean>", results, method(:computeGeometricMean), compareFunction, suffix, fieldSize)
+        printMean("<arithmetic mean>", results, method(:computeArithmeticMean), compareFunction, suffix, fieldSize)
+        printMean("<harmonic mean>", results, method(:computeHarmonicMean), compareFunction, suffix, fieldSize)
+
+        print "\n"
+    end
+
+    fieldSize = ($benchmarks + ["<arithmetic mean>"]).collect {
+        | benchmark |
+        benchmark.size
+    }.max + 4
+
+    printHeader(dylibs, fieldSize)
+    printMetric("Execution Time", results.executionTime, method(:lowerIsFaster), "ms", fieldSize)
+    printMetric("Peak Memory", results.peakMemory, method(:lowerIsSmaller), "kB", fieldSize)
+    printMetric("Memory at End", results.memoryAtEnd, method(:lowerIsSmaller), "kB", fieldSize)
+end
+
+def main
+    begin
+        dylibs = parseOptions()
+        results = runBenchmarks(dylibs)
+        printResults(dylibs, results)
+    rescue => exception
+        puts
+        puts
+        puts exception
+        puts exception.backtrace
+        puts
+    end
+end
+
+main()