function timer(f::Function, n::Integer)
    # Call once to force JIT compilation
    f()

    times = Array(Float64, n)
    for itr in 1:n
        times[itr] = @elapsed f()
    end

    return times
end

function benchmark(f::Function,
                   category::AbstractString,
                   name::AbstractString,
                   n::Integer)
    times = timer(f, n)

    df = DataFrame()
    df[:Category] = category
    df[:Benchmark] = name
    df[:Iterations] = n
    # TODO: Remove
    df[:TotalWall] = sum(times)
    df[:AverageWall] = mean(times)
    df[:MaxWall] = maximum(times)
    df[:MinWall] = minimum(times)
    df[:Timestamp] = Libc.strftime("%Y-%m-%d %H:%M:%S", round(Int, time()))
    df[:JuliaHash] = Base.GIT_VERSION_INFO.commit
    if isdir(".git")
        df[:CodeHash] = readchomp(`git rev-parse HEAD`)
    else
        df[:CodeHash] = NA
    end
    df[:OS] = string(OS_NAME)
    df[:CPUCores] = CPU_CORES

    return df
end

benchmark(f::Function, name::AbstractString, n::Integer) = benchmark(f, name, name, n)

function benchmarks(marks::Vector)
    df = DataFrame()

    for mark in marks
        df = vcat(df, benchmark(mark[1], mark[2], mark[3], mark[4]))
    end

    return df
end
