Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

[MXNET-145] Remove global PRNGs of numpy and python used in mx.image package #10575

Closed
wants to merge 2 commits into from

Conversation

asitstands
Copy link
Contributor

@asitstands asitstands commented Apr 17, 2018

Description

This PR replaces global random number generators of numpy and python used in mx.image package with mxnet's random number generators. This is a sequel of PR #10260.

Checklist

Essentials

Please feel free to remove inapplicable items for your PR.

  • The PR title starts with [MXNET-$JIRA_ID], where $JIRA_ID refers to the relevant JIRA issue created (except PRs with tiny changes)
  • Changes are complete (i.e. I finished coding on this PR)
  • All changes have test coverage:
  • Unit tests are added for small changes to verify correctness (e.g. adding a new operator)
  • Nightly tests are added for complicated/long-running ones (e.g. changing distributed kvstore)
  • Build tests will be added for build configuration changes (e.g. adding a new build option with NCCL)
  • Code is well-documented:
  • For user-facing API changes, API doc string has been updated.
  • For new C++ functions in header files, their functionalities and arguments are documented.
  • For new examples, README.md is added to explain the what the example does, the source of the dataset, expected performance on test set and reference to the original paper if applicable
  • Check the API doc at http://mxnet-ci-doc.s3-accelerate.dualstack.amazonaws.com/PR-$PR_ID/$BUILD_ID/index.html
  • To the my best knowledge, examples are either not affected by this change, or have been fixed to be compatible with this change

@asitstands asitstands requested a review from szha as a code owner April 17, 2018 02:09
@piiswrong
Copy link
Contributor

these are performance sensitive though. how much performance hit would this cause?

@asitstands
Copy link
Contributor Author

@piiswrong Currently I have no measure. Is there any benchmark or example code that I can use? If not, I'll write my own.

@asitstands
Copy link
Contributor Author

A simple benchmark using ImageIter shows a bad performance degradation. On Xeon E5-2680, the current master runs the following code in about 28s. The time is increased to 42s if this PR is applied. It is 50% increase of the running time.

start = time.time()
data = mx.img.ImageIter(
		batch_size=32,
		data_shape=(3, 224, 224),
		path_imgrec='caltech.rec', # 9144 image samples
		rand_crop=True,
		rand_resize=True,
		rand_mirror=True,
		rand_gray=0.5,
		brightness=0.5,
		contrast=0.5,
		saturation=0.5)
for i in data:
	pass
mx.nd.waitall()
end = time.time()
print("elapsed time: ", end - start)

@asitstands
Copy link
Contributor Author

asitstands commented Apr 17, 2018

Replacing the python/numpy global RNGs with a mxnet specific instance of numpy RNG would be better in terms of the performance. Is it acceptable keeping a numpy RNG as an internal utility in mx.random and seeding with mx.random.seed?

@asitstands
Copy link
Contributor Author

asitstands commented Apr 17, 2018

I'm not sure what causes the slowdown. If it is the overhead of mxnet's operator calling mechanism, adding a simple function to generate a scalar random number, using mxnet's internal RNGs, to the random number API would be the solution. I'll test with this.

@asitstands
Copy link
Contributor Author

asitstands commented Apr 17, 2018

I experimented with the following two functions. The first one takes 37s and the second one takes 28s to run the above ImageIter test. So the second one has the same performance with the implementation in the master using python RNG. The difference is from the serialization by the engine. I didn't expect that it causes such a large difference. To avoid the serialization, we need an independent RNG that is not intended for sharing. But I think that it should not be the global python/numpy RNG. I'll make a separate PR for this.

// Faster than sampling operator but still slow
int rand(mx_float low, mx_float high, mx_float* out) {
  API_BEGIN();
  Context ctx = Context::CPU();
  Engine::VarHandle var = Engine::Get()->NewVariable();
  mxnet::Resource resource = mxnet::ResourceManager::Get()->Request(
    ctx, ResourceRequest::kRandom);
  Engine::Get()->PushSync([low, high, out, resource](RunContext rctx) {
    mshadow::Random<mxnet::cpu, float> *prnd = 
      resource.get_random<mxnet::cpu, float>(rctx.get_stream<mxnet::cpu>());
    mshadow::Tensor<mxnet::cpu, 1, float> tmp(out, mshadow::Shape1(1));
    prnd->SampleUniform(&tmp, low, high);
  }, ctx, {}, {var, resource.var},
  FnProperty::kNormal, 0, PROFILER_MESSAGE_FUNCNAME);
  Engine::Get()->WaitForVar(var);
  Engine::Get()->DeleteVariable([](mxnet::RunContext) {}, mxnet::Context(), var);
  API_END();
}

// Fast but not safe as `resource` is shared
int rand(mx_float low, mx_float high, mx_float* out) {
  API_BEGIN();
  Context ctx = Context::CPU();
  mxnet::Resource resource = mxnet::ResourceManager::Get()->Request(
    ctx, ResourceRequest::kRandom);
  mshadow::Random<mxnet::cpu, float> *prnd = 
      resource.get_random<mxnet::cpu, float>(nullptr);
  mshadow::Tensor<mxnet::cpu, 1, float> tmp(out, mshadow::Shape1(1));
  prnd->SampleUniform(&tmp, low, high);
  API_END();
}

@piiswrong
Copy link
Contributor

I think an instance of numpy rng inside mxnet python package is good enough

@szha szha requested review from piiswrong and removed request for szha May 21, 2018 22:33
@asitstands asitstands closed this May 22, 2018
@asitstands asitstands deleted the rndfix_image branch May 22, 2018 07:16
@asitstands asitstands restored the rndfix_image branch May 22, 2018 07:19
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants