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

Apply cpu-optimisation to Rust projects #15544

Merged
merged 17 commits into from
Jul 4, 2023
Merged

Conversation

Tokarak
Copy link
Contributor

@Tokarak Tokarak commented Jun 13, 2023

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes? Here's an example.
  • Have you successfully run brew style with your changes locally?
  • Have you successfully run brew typecheck with your changes locally?
  • Have you successfully run brew tests with your changes locally?

Closes: #15530

@Tokarak
Copy link
Contributor Author

Tokarak commented Jun 13, 2023

My worry is that conditionally setting target-cpu via command line could start overwriting build caches (if homebrew uses them). This should be fine if there are no caches (:Fr:), or different cache folders based on version. Otherwise rust-lang/cargo#7773 is a problem.

@Tokarak
Copy link
Contributor Author

Tokarak commented Jun 13, 2023

Any idea how to test?

@Tokarak Tokarak marked this pull request as ready for review June 13, 2023 18:53
Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

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

Nice work so far!

# Override
# Mirrors version-dependent logic of oldest_cpu
sig { params(version: T.nilable(Version)).returns(String) }
def self.rustflags_target_cpu(version = nil)
Copy link
Member

Choose a reason for hiding this comment

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

When would version be passed through here rather than just use the current MacOS.version?

Library/Homebrew/extend/os/mac/hardware.rb Outdated Show resolved Hide resolved
MacOS.version
end
if Hardware::CPU.intel? && version >= :mojave
"'-Ctarget-cpu=nehalem'"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"'-Ctarget-cpu=nehalem'"
"--codegen target-cpu=nehalem"
  1. Let callers handle the quoting, if it's needed.
  2. Use the long version of the flag.
  3. I'd still rather use oldest_cpu here instead of hard-coding nehalem.

@@ -1543,7 +1543,8 @@ def std_configure_args
# Standard parameters for cargo builds.
sig { params(root: T.any(String, Pathname), path: String).returns(T::Array[T.any(String, Pathname)]) }
def std_cargo_args(root: prefix, path: ".")
["--locked", "--root", root, "--path", path]
["--locked", "--root", root, "--path", path, "--config",
"build.rustflags=[#{Hardware.rustflags_target_cpu}]"]
Copy link
Member

Choose a reason for hiding this comment

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

How does this interact with RUSTFLAGS set in the environment? Does it override them? Append to them?

I think we may prefer to set RUSTFLAGS in the environment as well as/instead of doing this, since not all rustc invocations happen through a cargo call that uses std_cargo_args. See, for example, gstreamer, or the many formulae that have a cryptography resource.

@Tokarak
Copy link
Contributor Author

Tokarak commented Jun 14, 2023

OK — new, prefered method: set RUSTFLAGS through a environment variable.

I think I'll add it to std env for now. I'm currently sanitising and overriding RUSTFLAGS.

  • Should this be in the super environment?
  • Should I prepend, append, or overwrite (thus sanitise)?
  • Is there a envvar for the target macos version, or does MacOs.version handle that?
  • Will there be an issue with caching, as conditional rustflags could start overwriting any compiled crates which had the opposite flag?
  • Testing!

Current review suggestions:

  • long option
 "-Ctarget-cpu=nehalem" ===>>>
 "--codegen target-cpu=nehalem"
  • Refactor rustflags_target_cpu to use oldest_cpu

Since Rust already maximins the cpu features, it's best to leave it to it. I think that a single hard-coding, 10 lines away from the other hardcoding is much less of a maintainance problem than an unsemantic dictionary (arm_vortex_tempest => 'apple-m1' ???) (think of the room for error too — what if oldest_cpu starts returning powerpc cpus, which are not available on any darwin target-triples. Plus nehalem is hardcoded in things like requires_nehalem?.
If you must insist, what is the goal of avoiding hardcoding? Is this way unsatisfactory?

@carlocab
Copy link
Member

  • Should this be in the super environment?

Yes.

  • Should I prepend, append, or overwrite (thus sanitise)?

Do the same thing as the other similar flags (e.g. CFLAGS), which I believe is overwrite.

  • Is there a envvar for the target macos version, or does MacOs.version handle that?

You can try HOMEBREW_MACOS_VERSION_NUMERIC but rely on MacOS.version where you can.

  • Will there be an issue with caching, as conditional rustflags could start overwriting any compiled crates which had the opposite flag?

Maybe, but cargo builds inside brew live inside HOMEBREW_CACHE so it shouldn't affect user usage of cargo.

  • Refactor rustflags_target_cpu to use oldest_cpu

Since Rust already maximins the cpu features, it's best to leave it to it. I think that a single hard-coding, 10 lines away from the other hardcoding is much less of a maintainance problem than an unsemantic dictionary (arm_vortex_tempest => 'apple-m1' ???) (think of the room for error too — what if oldest_cpu starts returning powerpc cpus, which are not available on any darwin target-triples. Plus nehalem is hardcoded in things like requires_nehalem?.
If you must insist, what is the goal of avoiding hardcoding? Is this way unsatisfactory?

Because having the same constants in multiple places (even when it they're not far away from each other) just isn't a good idea. The hypothetical situations you're worried about haven't arisen yet, and I don't think they will. If they do, then we can handle it then.

@Tokarak
Copy link
Contributor Author

Tokarak commented Jun 17, 2023

I now use oldest_cpu. I still fallback to rust defaults where I can.

Comment on lines 230 to 232
sig { returns(String) }
def rustflags_target_cpu
CPU.rust_optimisation_flags.fetch(oldest_cpu, "")
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
sig { returns(String) }
def rustflags_target_cpu
CPU.rust_optimisation_flags.fetch(oldest_cpu, "")
sig { returns(T.nilable(String)) }
def rustflags_target_cpu
CPU.rust_optimisation_flags.fetch(oldest_cpu, nil)

Also: we should try to support users doing something like

brew install --build-bottle --bottle-arch=ivybridge

which this doesn't currently do.

Comment on lines 49 to 50
native: "-Ctarget-cpu=native",
nehalem: "-Ctarget-cpu=nehalem",
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
native: "-Ctarget-cpu=native",
nehalem: "-Ctarget-cpu=nehalem",
native: "--codegen target-cpu=native",
nehalem: "--codegen target-cpu=nehalem",

Please use the long flags for better readability.

Comment on lines 44 to 46
# Only give values where it is an improvement over rust cpu defaults
# Rust already defaults to the oldest supported cpu for each target-triple
# Including apple-m1 since Rust 1.71 for aarch64-apple-darwin.
Copy link
Member

Choose a reason for hiding this comment

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

I'd really rather be explicit here rather than bake in assumptions about the behaviour of rustc. None of us track the development of rustc that carefully, so if they decide to bump the minimum of core2 before we do, then this method will no longer behave as expected, and that's likely to go unnoticed for some time.

The reverse (i.e. if we decide to bump the minimum of core2 before Rust does) is also an issue, albeit a smaller one.

Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

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

Looks good so far!

Library/Homebrew/hardware.rb Outdated Show resolved Hide resolved
Library/Homebrew/hardware.rb Outdated Show resolved Hide resolved
Library/Homebrew/hardware.rb Outdated Show resolved Hide resolved
Co-authored-by: Mike McQuaid <mike@mikemcquaid.com>
@Tokarak
Copy link
Contributor Author

Tokarak commented Jun 20, 2023

Looks like the CI failure is due to https://http://sequelpro.com/ being down.

Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

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

Looks good to me:

  1. if it works for you in local testing
  2. once @carlocab is also ✅

@Tokarak
Copy link
Contributor Author

Tokarak commented Jun 21, 2023

brew tests --changed is fine.

@carlocab
Copy link
Member

I'm currently neck-deep in Homebrew/homebrew-core#134251, so I haven't had a chance to take a proper look here. Will come back to it next week.

Co-authored-by: Alexander Bayandin <alexander@bayandin.dev>
Copy link
Member

@carlocab carlocab left a comment

Choose a reason for hiding this comment

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

Thanks; I'm happy with how this is looking currently.

Bonus points for the following (but you can save it for a future PR):

  1. Support for brew install --build-bottle --bottle-arch=<custom-arch>
  2. An ENV.rustflags accessor, just like what we have for ENV.cflags, ENV.ldflags, etc.

Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

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

Thanks so much for your first contribution (hopefully of many)! Without people like you submitting PRs we couldn't run this project. You rock, @Tokarak!

@BugenZhao
Copy link

BugenZhao commented Jul 9, 2023

Hi, all. According to The Cargo Book, setting the RUSTFLAGS environment variable will override any RUSTFLAGS specified in a cargo package's config file.

This may cause some packages to fail to compile if they're using RUSTFLAGS to enable some features, like tokio_unstable. For example, RisingWave relies on tokio's several unstable features and users find it fail to build after upgrading brew to 4.0.27.

https://github.com/risingwavelabs/risingwave/blob/f03dbc62c4e07fd16164a6b81219d3ffd5f86fa9/.cargo/config.toml#L32-L36
risingwavelabs/homebrew-risingwave#10
https://docs.rs/tokio/latest/tokio/task/fn.consume_budget.html

@carlocab
Copy link
Member

I suggest doing

ENV.delete "RUSTFLAGS"

in the install method of your formula if you need to set build.rustflags in your config.toml.

@github-actions github-actions bot added the outdated PR was locked due to age label Aug 10, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 10, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated PR was locked due to age
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Optimise rust-based formulas by setting target-cpu
5 participants