Commit Graph

203 Commits

Author SHA1 Message Date
Jatin Chaudhary
47a5f77d75 #722 Adding Host Name in Reporting (#733)
* Adding Host Name and test

* Addressing Review Comments

* Adding Test for JSON Reporter

* Adding HOST_NAME_MAX for MacOS systems

* Adding Explaination for MacOS HOST_NAME_MAX Addition

* Addressing Peer Review Comments

* Adding codecvt in windows header guard

* Changing name SystemInfo and adding empty message incase host name fetch fails

* Adding Comment on Struct SystemInfo
2018-12-11 11:23:02 +00:00
Dominic Hamon
b5082bbd65 Merge branch 'report_loadavg' of https://github.com/atdt/benchmark into atdt-report_loadavg 2018-11-13 10:13:58 +00:00
Victor Costan
d8c0f27448 Fix possible loss of data warnings in MSVC. (#694) 2018-10-01 13:00:13 +01:00
Dominic Hamon
edc77a3669
Make State constructor private. (#650)
The State constructor should not be part of the public API. Adding a
utility method to BenchmarkInstance allows us to avoid leaking the
RunInThread method into the public API.
2018-09-28 12:28:43 +01:00
Roman Lebedev
1b44120cd1
Un-deprecate [SG]et{Item,Byte}sProcessed, re-implement as custom counters. (#676)
As discussed with @dominichamon and @dbabokin, sugar is nice.
Well, maybe not for the health, but it's sweet.
Alright, enough puns.

A special care needs to be applied not to break csv reporter. UGH.
We end up shedding some code over this.
We no longer specially pretty-print them, they are printed just like the rest of custom counters.

Fixes #627.
2018-09-13 22:03:47 +03:00
Roman Lebedev
58588476ce
Track two more details about runs - the aggregate name, and run name. (#675)
This is related to @BaaMeow's work in https://github.com/google/benchmark/pull/616 but is not based on it.

Two new fields are tracked, and dumped into JSON:
* If the run is an aggregate, the aggregate's name is stored.
  It can be RMS, BigO, mean, median, stddev, or any custom stat name.
* The aggregate-name-less run name is additionally stored.
  I.e. not some name of the benchmark function, but the actual
  name, but without the 'aggregate name' suffix.

This way one can group/filter all the runs,
and filter by the particular aggregate type.

I *might* need this for further tooling improvement.
Or maybe not.
But this is certainly worthwhile for custom tooling.
2018-09-13 15:08:15 +03:00
Roman Lebedev
c614dfc0d4
*Display* aggregates only. (#665)
There is a flag 
d9cab612e4/src/benchmark.cc (L75-L78)
and a call
d9cab612e4/include/benchmark/benchmark.h (L837-L840)
But that affects everything, every reporter, destination:
d9cab612e4/src/benchmark.cc (L316)


It would be quite useful to have an ability to be more picky.


More specifically, i would like to be able to only see the aggregates in the on-screen output,
but for the file output to still contain everything. The former is useful in case of a lot of repetition
(or even more so if every iteration is reported separately), while the former is **great** for tooling.

Fixes https://github.com/google/benchmark/issues/664
2018-09-12 16:26:17 +03:00
Changming Sun
305ba313be Pass name by const-reference instead of by value in class Statistics' constructor (#668) 2018-09-04 23:46:40 +03:00
Roman Lebedev
5159967520
Mark Set{Items,Bytes}Processed()/{items,bytes}_processed() as deprecated. (#654)
They are basically proto-version of custom user counters.
It does not seem that they do anything that custom user counters
don't do. And having two similar entities is not good for generalization.

Migration plan:
* ```
  SetItemsProcessed(<val>)
    =>
  state.counters.insert({
    {"<Name>", benchmark::Counter(<val>, benchmark::Counter::kIsRate)},
    ...
  });
  ```
* ```
  SetBytesProcessed(<val>)
    =>
  state.counters.insert({
    {"<Name>", benchmark::Counter(<val>, benchmark::Counter::kIsRate,
                                  benchmark::Counter::OneK::kIs1024)},
    ...
  });
  ```
* ```
  <Name>_processed()
    =>
  state.counters["<Name>"]
  ```

One thing the custom user counters miss is better support
for units of measurement.

Refs. https://github.com/google/benchmark/issues/627
2018-08-30 11:59:50 +03:00
Roman Lebedev
caa2fcb19c
Counter(): add 'one thousand' param. (#657)
* Counter(): add 'one thousand' param.

Needed for https://github.com/google/benchmark/pull/654

Custom user counters are quite custom. It is not guaranteed
that the user *always* expects for these to have 1k == 1000.
If the counter represents bytes/memory/etc, 1k should be 1024.

Some bikeshedding points:
1. Is this sufficient, or do we really want to go full on
   into custom types with names?
   I think just the '1000' is sufficient for now.
2. Should there be a helper benchmark::Counter::Counter{1000,1024}()
   static 'constructor' functions, since these two, by far,
   will be the most used?
3. In the future, we should be somehow encoding this info into JSON.

* Counter(): use std::pair<> to represent 'one thousand'

* Counter(): just use a new enum with two values 1000 vs 1024.

Simpler is better. If someone comes up with a real reason
to need something more advanced, it can be added later on.

* Counter: just store the 1000 or 1024 in the One_K values directly

* Counter: s/One_K/OneK/
2018-08-29 21:11:06 +03:00
Roman Lebedev
d9cab612e4
[NFC] s/console_reporter/display_reporter/ (#663)
There are two destinations:
* display (console, terminal) and
* file.

And each of the destinations can be poplulated with one of the reporters:
* console - human-friendly table-like display
* json
* csv (deprecated)

So using the name console_reporter is confusing.
Is it talking about the console reporter in the sense of
table-like reporter, or in the sense of display destination?
2018-08-29 14:58:54 +03:00
Roman Lebedev
8688c5c4cf
Track 'type' of the run - is it an actual measurement, or an aggregate. (#658)
This is *only* exposed in the JSON. Not in CSV, which is deprecated.

This *only* supposed to track these two states.
An additional field could later track which aggregate this is,
specifically (statistic name, rms, bigo, ...)

The motivation is that we already have ReportAggregatesOnly,
but it affects the entire reports, both the display,
and the reporters (json files), which isn't ideal.

It would be very useful to have a 'display aggregates only' option,
both in the library's console reporter, and the python tooling,
This will be especially needed for the 'store separate iterations'.
2018-08-28 18:11:36 +03:00
Roman Lebedev
9a179cb93f
[NFC] Prefix "report(_)?mode" with Aggregation. (#656)
This only specifically represents handling of reporting of aggregates.
Not of anything else. Making it more specific makes the name less generic.

This is an issue because i want to add "iteration report mode",
so the naming would be conflicting.
2018-08-28 17:19:25 +03:00
Kirill Bobyrev
f85304e4e3 Remove redundant default which causes failures (#649)
* Remove redundant default which causes failures

* Fix old GCC warnings caused by poor analysis

* Use __builtin_unreachable

* Use BENCHMARK_UNREACHABLE()

* Pull __has_builtin to benchmark.h too

* Also move compiler identification macro to main header

* Move custom compiler identification macro back
2018-08-08 14:39:57 +01:00
Dominic Hamon
f965eab508
Memory management and reporting hooks (#625)
* Introduce memory manager interface

* Add memory stats to JSON reporter and a test

* Add comments and switch json output test to int
2018-07-24 15:57:15 +01:00
Ori Livneh
da9ec3dfca Include system load average in console and JSON reports
High system load can skew benchmark results. By including system load averages
in the library's output, we help users identify a potential issue in the
quality of their measurements, and thus assist them in producing better (more
reproducible) results.

I got the idea for this from Brendan Gregg's checklist for benchmark accuracy
(http://www.brendangregg.com/blog/2018-06-30/benchmarking-checklist.html).
2018-07-09 10:51:08 -04:00
Roman Lebedev
b123abdcf4 Add Iteration-related Counter::Flags. Fixes #618 (#621)
Inspired by these [two](a1ebe07bea) [bugs](0891555be5) in my code due to the lack of those i have found fixed in my code:
* `kIsIterationInvariant` - `* state.iterations()`
  The value is constant for every iteration, and needs to be **multiplied** by the iteration count.
* `kAvgIterations` - `/ state.iterations()`
  The is global over all the iterations, and needs to be **divided** by the iteration count.

They play nice with `kIsRate`:
* `kIsIterationInvariantRate`
* `kAvgIterationsRate`.

I'm not sure how  meaningful they are when combined with `kAvgThreads`.
I guess the `kIsThreadInvariant` can be added, too, for symmetry with `kAvgThreads`.
2018-06-27 15:45:30 +01:00
Dominic Hamon
151ead6242
Disable deprecation warnings when -Werror is enabled. (#609)
Fixes #608
2018-06-07 12:54:14 +01:00
Dominic Hamon
d07372e64b
clang-format run on the benchmark header (#606) 2018-05-29 14:12:51 +01:00
Eric
7b8d0249d8 Deprecate CSVReporter - A first step to overhauling reporting. (#488)
As @dominichamon and I have discussed, the current reporter interface
is poor at best. And something should be done to fix it.

I strongly suspect such a fix will require an entire reimagining
of the API, and therefore breaking backwards compatibility fully.

For that reason we should start deprecating and removing parts
that we don't intend to replace. One of these parts, I argue,
is the CSVReporter. I propose that the new reporter interface
should choose a single output format (JSON) and traffic entirely
in that. If somebody really wanted to replace the functionality
of the CSVReporter they would do so as an external tool which
transforms the JSON.

For these reasons I propose deprecating the CSVReporter.
2018-05-29 13:25:32 +01:00
Samuel Panzer
ce3fde16cb Return 0 from State::iterations() when not yet started. (#598)
* Return a reasonable value from State::iterations() even before starting a benchmark

* Optimize State::iterations() for started case.
2018-05-24 10:33:19 +01:00
Sam Clegg
8986839e4a Use __EMSCRIPTEN__ (rather then EMSCRIPTEN) to check for emscripten (#583)
The old EMSCRIPTEN macro is deprecated and not enabled when
EMCC_STRICT is set.

Also fix a typo in EMSCRIPTN (not sure how this ever worked).
2018-05-03 09:34:26 +01:00
Dominic Hamon
64e5a13fa0
Ensure 64-bit truncation doesn't happen for complexity_n (#569)
* Ensure 64-bit truncation doesn't happen for complexity results

* One more complexity_n 64-bit fix

* Missed another vector of int

* Piping through the int64_t
2018-04-12 15:40:24 +01:00
Dominic Hamon
9913418d32
Allow AddRange to work with int64_t. (#548)
* Allow AddRange to work with int64_t.

Fixes #516

Also, tweak how we manage per-test build needs, and create a standard
_gtest suffix for googletest to differentiate from non-googletest tests.

I also ran clang-format on the files that I changed (but not the
benchmark include or main src as they have too many clang-format
issues).

* Add benchmark_gtest to cmake

* Set(Items|Bytes)Processed now take int64_t
2018-04-03 23:12:47 +01:00
Eric
7b03df7ff7
Add tests to verify assembler output -- Fix DoNotOptimize. (#530)
* Add tests to verify assembler output -- Fix DoNotOptimize.

For things like `DoNotOptimize`, `ClobberMemory`, and even `KeepRunning()`,
it is important exactly what assembly they generate. However, we currently
have no way to test this. Instead it must be manually validated every
time a change occurs -- including a change in compiler version.

This patch attempts to introduce a way to test the assembled output automatically.
It's mirrors how LLVM verifies compiler output, and it uses LLVM FileCheck to run
the tests in a similar way.

The tests function by generating the assembly for a test in CMake, and then
using FileCheck to verify the // CHECK lines in the source file are found
in the generated assembly.

Currently, the tests only run on 64-bit x86 systems under GCC and Clang,
and when FileCheck is found on the system.

Additionally, this patch tries to improve the code gen from DoNotOptimize.
This should probably be a separate change, but I needed something to test.

* Disable assembly tests on Bazel for now

* Link FIXME to github issue

* Fix Tests on OS X

* fix strip_asm.py to work on both Linux and OS X like targets
2018-03-23 16:10:47 -06:00
Wink Saville
61497236dd Make string_util naming more consistent (#547)
* Rename StringXxx to StrXxx in string_util.h and its users

This makes the naming consistent within string_util and moves is the
Abseil convention.

* Style guide is 2 spaces before end of line "//" comments

* Rename StrPrintF/StringPrintF to StrFormat for absl compatibility.
2018-03-07 11:20:06 +00:00
Eric
56f52ee228 Print the executable name as part of the context. (#534)
* Print the executable name as part of the context.

A common use case of the library is to run two different
versions of a benchmark to compare them. In my experience
this often means compiling a benchmark twice, renaming
one of the executables, and then running the executables
back-to-back. In this case the name of the executable
is important contextually information.  Unfortunately the
benchmark does not report this information.

This patch adds the executable name to the context reported
by the benchmark.

* attempt to fix tests on Windows

* attempt to fix tests on Windows
2018-02-21 08:43:57 -08:00
Eric Fiselier
858688b845 Ensure std::iterator_traits<StateIterator> instantiates.
Due to ADL lookup performed on the begin and end functions
of `for (auto _ : State)`, std::iterator_traits may get
incidentally instantiated. This patch ensures the library
can tolerate that.
2018-02-21 00:54:19 -07:00
Eric
207b9c7aec
Improve State packing: put important members on first cache line. (#527)
* Improve State packing: put important members on first cache line.

This patch does a few different things to ensure commonly accessed
data is on the first cache line of the `State` object.

First, it moves the `error_occurred_` member to reside after
the `started_` and `finished_` bools, since there was internal
padding there that was unused.

Second, it moves `batch_leftover_` and `max_iterations` further up
in the struct declaration. These variables are used in the calculation
of `iterations()` which users might call within the loop. Therefore
it's more important they exist on the first cache line.

Finally, this patch turns the bool members into bitfields. Although
this shouldn't have much of an effect currently, because padding is
still needed between the last bool and the first size_t, it should
help in future changes that require more "bool like" members.

* Remove bitfield change for now

* Move bools (and their padding) to end of "first cache line" vars.

I think it makes the most sense to move the padding required
following the group of bools to the end of the variables we want
on the first cache line.

This also means that the `total_iterations_` variable, which is the
most accessed, has the same address as the State object.

* Fix static assertion after moving bools
2018-02-14 13:44:41 -07:00
Samuel Panzer
3924ee7b8a Fixups following addition of KeepRunningBatch (296ec5693) (#526)
* Support State::KeepRunningBatch().

State::KeepRunning() can take large amounts of time relative to quick
operations (on the order of 1ns, depending on hardware). For such
sensitive operations, it is recommended to run batches of repeated
operations.

This commit simplifies handling of total_iterations_. Rather than
predecrementing such that total_iterations_ == 1 signals that
KeepRunning() should exit, total_iterations_ == 0 now signals the
intention for the benchmark to exit.

* Create better fast path in State::KeepRunningBatch()

* Replace int parameter with size_t to fix signed mismatch warnings

* Ensure benchmark State has been started even on error.

* Simplify KeepRunningBatch()

* Implement KeepRunning() in terms of KeepRunningBatch().

* Improve codegen by helping the compiler undestand dead code.

* Dummy commit for build bots' benefit.
2018-02-13 13:54:46 -07:00
Samuel Panzer
296ec5693e Support State::KeepRunningBatch(). (#521)
* Support State::KeepRunningBatch().

State::KeepRunning() can take large amounts of time relative to quick
operations (on the order of 1ns, depending on hardware). For such
sensitive operations, it is recommended to run batches of repeated
operations.

This commit simplifies handling of total_iterations_. Rather than
predecrementing such that total_iterations_ == 1 signals that
KeepRunning() should exit, total_iterations_ == 0 now signals the
intention for the benchmark to exit.

* Create better fast path in State::KeepRunningBatch()

* Replace int parameter with size_t to fix signed mismatch warnings

* Ensure benchmark State has been started even on error.

* Simplify KeepRunningBatch()
2018-02-09 21:57:04 -07:00
Louis Dionne
5b2c08668c Enforce using a semicolon after BENCHMARK_MAIN to remove compiler warnings (#495) 2017-12-03 18:45:07 -07:00
Eric
11dc36822b
Improve CPU Cache info reporting -- Add Windows support. (#486)
* Improve CPU Cache info reporting -- Add Windows support.

This patch does a couple of thing regarding CPU Cache reporting.

First, it adds an implementation on Windows. Second it fixes
the JSONReporter to correctly (and actually) output the CPU
configuration information.

And finally, third, it detects and reports the number of
physical CPU's that share the same cache.
2017-11-26 13:33:01 -07:00
Eric
27e0b439cf Refactor System information collection -- Add CPU Cache Info (#483)
* Refactor System information collection.

This patch refactors the system information collection,
and in particular information about the target CPU. The
motivation is to make it easier to access CPU information,
and easier to add new information as need be.

This patch additionally adds information about the cache
sizes of the CPU.

* Address review comments: Clean up integer types.

This commit cleans up the integer types used in ValueUnion to
follow the Google style guide.

Additionally it adds a BENCHMARK_UNREACHABLE macro to assist
in documenting/catching unreachable code paths.

* Rename ValueUnion accessors.
2017-11-22 08:33:52 -08:00
Dominic Hamon
90aa8665b5
Reorder inline to avoid warning on MSVC (#469)
Fixes #467
2017-11-07 10:33:07 -08:00
Dominic Hamon
f4009ef8e3
Fix #476. Explicit coersion of size_t to boolean (#477) 2017-11-07 10:30:17 -08:00
Dominic Hamon
f65c6d9a2c
Remove deprecated headers (#473) 2017-11-06 08:53:23 -08:00
Leo Koppel
fa341e51cb Improve BM_SetInsert example (#465)
* Fix BM_SetInsert example

Move declaration of `std::set<int> data` outside the timing loop, so that the
destructor is not timed.

* Speed up BM_SetInsert test

Since the time taken to ConstructRandomSet() is so large compared to the time
to insert one element, but only the latter is used to determine number of
iterations, this benchmark now takes an extremely long time to run in
benchmark_test.

Speed it up two ways:
  - Increase the Ranges() parameters
  - Cache ConstructRandomSet() result (it's not random anyway), and do only
    O(N) copy every iteration

* Fix same issue in BM_MapLookup test

* Make BM_SetInsert test consistent with README

- Use the same Ranges everywhere, but increase the 2nd range
- Change order of Args() calls in README to more closely match the result of Ranges
- Don't cache ConstructRandomSet, since it doesn't make sense in README
- Get a smaller optimization inside it, by givint a hint to insert()
2017-10-31 11:00:39 -07:00
Eric
25acf220a4 Refactor most usages of KeepRunning to use the perfered ranged-for. (#459)
Recently the library added a new ranged-for variant of the KeepRunning
loop that is much faster. For this reason it should be preferred in all
new code.

Because a library, its documentation, and its tests should all embody
the best practices of using the library, this patch changes all but a
few usages of KeepRunning() into for (auto _ : state).

The remaining usages in the tests and documentation persist only
to document and test behavior that is different between the two formulations.

Also note that because the range-for loop requires C++11, the KeepRunning
variant has not been deprecated at this time.
2017-10-17 12:17:02 -06:00
Eric Fiselier
22fd1a556e Fix and document SkipWithError(...) using ranged-for loop. 2017-10-17 10:24:13 -06:00
Eric
a37fc0c48a Improve KeepRunning loop performance to be similar to the range-based for. (#460)
This patch improves the performance of the KeepRunning loop in two ways:

(A) it removes the dependency on the max_iterations variable, preventing
it from being loaded every iteration.

(B) it loops to zero, instead of to an upper bound. This allows a single
decrement instruction to be used instead of a arithmetic op followed by a
comparison.
2017-10-17 08:40:44 -07:00
Eric
0526755944 Add C++11 Ranged For loop alternative to KeepRunning (#454)
* Add C++11 Ranged For loop alternative to KeepRunning

As pointed out by @astrelni and @dominichamon, the KeepRunning
loop requires a bunch of memory loads and stores every iterations,
which affects the measurements.

The main reason for these additional loads and stores is that the
State object is passed in by reference, making its contents externally
visible memory, and the compiler doesn't know it hasn't been changed
by non-visible code.

It's also possible the large size of the State struct is hindering
optimizations.

This patch allows the `State` object to be iterated over using
a range-based for loop. Example:

void BM_Foo(benchmark::State& state) {
	for (auto _ : state) {
		[...]
	}
}

This formulation is much more efficient, because the variable counting
the loop index is stored in the iterator produced by `State::begin()`,
which itself is stored in function-local memory and therefore not accessible
by code outside of the function. Therefore the compiler knows the iterator
hasn't been changed every iteration.

This initial patch and idea was from Alex Strelnikov.

* Fix null pointer initialization in C++03
2017-10-10 08:56:42 -07:00
mwinterb
f3cd636f18 Always use inline asm DoNotOptimize with clang. (#452)
* Always use inline asm DoNotOptimize with clang.

clang-cl masquerades as MSVC but not GCC, so it was using the
MSVC-compatible definitions of DoNotOptimize and ClobberMemory.
Presumably, it's better in general to use the targeted assembly for
this functionality (the codegen is different), but the specific issue
is that clang-cl deprecates the usage of _ReadWriteBarrier, and this
gets rid of that warning.

* triggering another AppVeyor run
2017-10-10 00:19:01 +02:00
Anton Lashkov
819adb4cd1 Add macros for create benchmark with templated fixture (#451)
* Add macros for create benchmark with templated fixture

* Add info about templated fixtures to README.md

* Add tests for templated fixtures
2017-10-09 21:10:37 +02:00
Eric
6d8339dd97 Fix #444 - Use BENCHMARK_HAS_CXX11 over __cplusplus. (#446)
* Fix #444 - Use BENCHMARK_HAS_CXX11 over __cplusplus.

MSVC incorrectly defines __cplusplus to report C++03, despite the compiler
actually providing C++11 or greater. Therefore we have to detect C++11 differently
for MSVC. This patch uses `_MSVC_LANG` which has been defined since
Visual Studio 2015 Update 3; which should be sufficient for detecting C++11.

Secondly this patch changes over most usages of __cplusplus >= 201103L to
check BENCHMARK_HAS_CXX11 instead.

* remove redunant comment
2017-09-14 15:50:33 -06:00
Roman Lebedev
a271c36af9 Drop Stat1, refactor statistics to be user-providable, add median. (#428)
* Drop Stat1, refactor statistics to be user-providable, add median.

My main goal was to add median statistic. Since Stat1
calculated the stats incrementally, and did not store
the values themselves, it is was not possible. Thus,
i have replaced Stat1 with simple std::vector<double>,
containing all the values.

Then, i have refactored current mean/stdev to be a
function that is provided with values vector, and
returns the statistic. While there, it seemed to make
sense to deduplicate the code by storing all the
statistics functions in a map, and then simply iterate
over it. And the interface to add new statistics is
intentionally exposed, so they may be added easily.

The notable change is that Iterations are no longer
displayed as 0 for stdev. Is could be changed, but
i'm not sure how to nicely fit that into the API.

Similarly, this dance about sometimes (for some fields,
for some statistics) dividing by run.iterations, and
then multiplying the calculated stastic back is also
dropped, and if you do the math, i fail to see why
it was needed there in the first place.

Since that was the only use of stat.h, it is removed.

* complexity.h: attempt to fix MSVC build

* Update README.md

* Store statistics to compute in a vector, ensures ordering.

* Add a bit more tests for repetitions.

* Partially address review notes.

* Fix gcc build: drop extra ';'

clang, why didn't you warn me?

* Address review comments.

* double() -> 0.0
* early return
2017-08-23 16:44:29 -07:00
Dominic Hamon
5b7683f49e more clang tidy cleanups (#417) 2017-07-15 00:21:20 +02:00
Dominic Hamon
e8fc2a2b8c Google-style cleanups (#416) 2017-07-13 18:33:43 +02:00
Eric
9d4b719dae Make Benchmark a single header library (but not header-only) (#407)
* Make Benchmark a single header library (but not header-only)

This patch refactors benchmark into a single header, to allow
for slightly easier usage.

The initial reason for the header split was to keep C++ library
components from being included by benchmark_api.h, making that
part of the library STL agnostic. However this has since changed
and there seems to be little reason to separate the reporters from
the rest of the library.

* Fix internal_macros.h

* Remove more references to macros.h
2017-07-04 16:31:47 -06:00
Eric
b8a2206fb2 Add ClearRegisteredBenchmark() function. (#402)
* Add ClearRegisteredBenchmark() function.

Since benchmarks can be registered at runtime using the RegisterBenchmark(...)
functions, it makes sense to have a ClearRegisteredBenchmarks() function too,
that can be used at runtime to clear the currently registered benchmark and
re-register an entirely new set.

This allows users to run a set of registered benchmarks, get the output using
a custom reporter, and then clear and re-register new benchmarks based on the
previous results.

This fixes issue #400, at least partially.

* Remove unused change
2017-06-14 09:16:53 -07:00