Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove duplicated Module.Route calls #7716

Merged
merged 3 commits into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions types/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,11 @@ func (m *Manager) RegisterInvariants(ir sdk.InvariantRegistry) {
// RegisterRoutes registers all module routes and module querier routes
func (m *Manager) RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino) {
for _, module := range m.Modules {
if !module.Route().Empty() {
router.AddRoute(module.Route())
if r := module.Route(); !r.Empty() {
Copy link
Contributor

@alessio alessio Oct 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see much benefit here. Using methods allow the cache to kick in and you spare memory allocation. Not a big deal, it just looks a bit of a pointless change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't think these changes are necessary. Makes it generally harder to read. My rule of thumb is typically, if a value is used more than once, put it in a variable. I'd revert these changes.

Copy link
Collaborator Author

@robert-zaremba robert-zaremba Oct 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used more than once. See the line below. That's why I did that change. PRO TIP: see also the PR description.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PRO TIP: see also the PR description.

I saw it. Problem is that you didn't see my Using methods allow the cache to kick in and you spare memory allocation..
My advice is read it again. And again, should that help.

Please revert.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When imperative, not jitted methods are cached? Do you have any resource about it, especially related to Go? I would imagine that JIT could do it. But Go doesn't have a JIT.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolutely right, my bad. I think this change set is fine OK.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexanderbez

Copy link
Collaborator Author

@robert-zaremba robert-zaremba Oct 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alessio - of course this is changeset is minor. Like I wrote, I did it because I was in this function while tracing an app and noticed it. If I wouldn't do the change with Trim, I would probably skip it. But it was few seconds to do that change.
Also, I don't believe that any function call cache validator will be smarter and faster than just using this reference - it's local and it will stay in the CPU cache anyway. Statically compiled, imperative languages don't do that - even on hardware, the the processor will need to know the whole context of the call. Especially here the module has a complex object and it can't mutate. To use a runtime function call cache you will need to assert that nothing changed. JIT or pure languages could do that.

I don't understand why do you like to bash the work I'm doing. I was proving that error checking is kind of critical. It's easier to do few not necessary error checks then be cautious to not forget a crucial check or wrongly assigned error. That's why we have tools for that. During the SDK call when I mentioned about errcheck nobody was against. Instead of spending hours of yours and mine time criticizing you could also write test cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disclaimer #1: I've approved this PR already. Since the author seems to enjoy some pointless debate though, I feel compelled to entertain this circus for a tad longer.

Disclaimer #2: This is my last response in this thread. I have quite a number of things on my plate and I cannot afford any further meaningless distractions.

of course this is changeset is minor.

Is it? Sounds like a astdounding revelation. /me truly gobsmacked.

I don't believe that any function call cache validator will be smarter and faster than just using this reference

Whether you believe it or not does not change that you can't predict what happens at runtime beforehand, once it's compiled on either your machine, my machine or someone else's machine. Fact.

it's local and it will stay in the CPU cache anyway

That too proves another point of mine (just-a-cosmetic-change). Bingo (once more).

Statically compiled, imperative languages don't do that - even on hardware, the the processor will need to know the whole context of the call. Especially here the module has a complex object and it can't mutate.

Again, this proves nothing for you can't predict on all target platforms what happens at runtime unless you dig into the binary's assembly for each platform this tiny, insignificant piece of code will be compiled.

I approved this PR. I warmly advise against tilting at windmills. There is a whole ambitious Cosmos SDK testing tools & sandbox work plan drafted by this PR's author that will hardly be implemented by just staring at it.

'Been fun.

AT

Copy link
Collaborator Author

@robert-zaremba robert-zaremba Oct 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That too proves another point of mine (just-a-cosmetic-change). Bingo (once more).

It is not. Don't quote fragments of my post without a context, please. r := funWithComplexDependencies(); doSomeithg(r) is totally different in not-jitted, imperative languages than funWithComplexDependencies(); doSomething(funWithComplexDependencies()).

router.AddRoute(r)
}
if module.QuerierRoute() != "" {
queryRouter.AddRoute(module.QuerierRoute(), module.LegacyQuerierHandler(legacyQuerierCdc))
if r := module.QuerierRoute(); r != "" {
robert-zaremba marked this conversation as resolved.
Show resolved Hide resolved
queryRouter.AddRoute(r, module.LegacyQuerierHandler(legacyQuerierCdc))
}
}
}
Expand Down
18 changes: 7 additions & 11 deletions types/module/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,15 @@ func TestManager_RegisterRoutes(t *testing.T) {
require.Equal(t, 2, len(mm.Modules))

router := mocks.NewMockRouter(mockCtrl)
handler1, handler2 := sdk.Handler(func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
return nil, nil
}), sdk.Handler(func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
return nil, nil
})
route1 := sdk.NewRoute("route1", handler1)
route2 := sdk.NewRoute("route2", handler2)
mockAppModule1.EXPECT().Route().Times(2).Return(route1)
mockAppModule2.EXPECT().Route().Times(2).Return(route2)
router.EXPECT().AddRoute(gomock.Any()).Times(2) // Use of Any due to limitations to compare Functions as the sdk.Handler
noopHandler := sdk.Handler(func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { return nil, nil })
route1 := sdk.NewRoute("route1", noopHandler)
route2 := sdk.NewRoute("", noopHandler)
mockAppModule1.EXPECT().Route().Times(1).Return(route1)
mockAppModule2.EXPECT().Route().Times(1).Return(route2)
router.EXPECT().AddRoute(gomock.Any()).Times(1) // Use of Any due to limitations to compare Functions as the sdk.Handler

queryRouter := mocks.NewMockQueryRouter(mockCtrl)
mockAppModule1.EXPECT().QuerierRoute().Times(2).Return("querierRoute1")
mockAppModule1.EXPECT().QuerierRoute().Times(1).Return("querierRoute1")
mockAppModule2.EXPECT().QuerierRoute().Times(1).Return("")
handler3 := sdk.Querier(nil)
amino := codec.NewLegacyAmino()
Expand Down
4 changes: 2 additions & 2 deletions types/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type Route struct {

// NewRoute returns an instance of Route.
func NewRoute(p string, h Handler) Route {
return Route{path: p, handler: h}
return Route{path: strings.TrimSpace(p), handler: h}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if I should do the TrimSpace in line 58, but on r.path or just move it here.
I move it here for a hygiene.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is OK 👍

}

// Path returns the path the route has assigned.
Expand All @@ -55,7 +55,7 @@ func (r Route) Handler() Handler {

// Empty returns true only if both handler and path are not empty.
func (r Route) Empty() bool {
return r.handler == nil || r.path == strings.TrimSpace("")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strings.TrimSpace("") doesn't make any sense. It should be on path. I move trimming to line 43.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense

return r.handler == nil || r.path == ""
}

// QueryRouter provides queryables for each query path.
Expand Down