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

sage-logger: Replace use of /usr/bin/time by bash keyword time #37840

Merged
merged 1 commit into from
May 25, 2024

Conversation

mkoeppe
Copy link
Contributor

@mkoeppe mkoeppe commented Apr 20, 2024

Follow-up after

... to make the display of package build times available on Linux too.

Fixes #37785 (comment), closes #37832.

📝 Checklist

  • The title is concise and informative.
  • The description explains in detail what this PR is about.
  • I have linked a relevant issue or discussion.
  • I have created tests covering the changes.
  • I have updated the documentation and checked the documentation preview.

⌛ Dependencies

@mkoeppe mkoeppe self-assigned this Apr 20, 2024
@mkoeppe mkoeppe requested a review from tornaria April 20, 2024 23:45
@mkoeppe mkoeppe marked this pull request as ready for review April 20, 2024 23:45
Copy link

github-actions bot commented Apr 20, 2024

Documentation preview for this PR (built with commit ffa4f46; changes) is ready! 🎉
This preview will update shortly after each push to this PR.

@tornaria
Copy link
Contributor

Do we really need to have different functions for different systems? It seems this kind of case by case handling might lead to issues later.

May I suggest something like this, which depends only on $SECONDS (available on bash) and will use whatever the user has as time (usually bash builtin)

time_silent_if_less_than() { 
  local max=$(($SECONDS+$1))
  shift;
  exec 3>&1 4>&2
  local out=$((time 1>&3 2>&4 "$@") 2>&1)
  [ "$SECONDS" -lt "$max" ] || echo "$out" >&2
}

Usage:

$ time_silent_if_less_than 10 sh -c "sleep 5 ; echo stdout && echo stderr >&2" 
stdout
stderr
$ time_silent_if_less_than 10 sh -c "sleep 15 ; echo stdout && echo stderr >&2" 
stdout
stderr

real	0m15.006s
user	0m0.001s
sys	0m0.005s
$ time_silent_if_less_than 2 sh -c "sleep 65 ; echo stdout && echo stderr >&2" 
stdout
stderr

real	1m5.007s
user	0m0.001s
sys	0m0.006s

Variation:

time_silent_if_less_than() { 
  local max=$(($SECONDS+$1))
  shift;
  exec 3>&1 4>&2
  local out=$((time 1>&3 2>&4 "$@") 2>&1)
  [ "$SECONDS" -lt "$max" ] || echo $out >&2
}
$ time_silent_if_less_than 10 sh -c "sleep 15 ; echo stdout && echo stderr >&2" 
stdout
stderr
real 0m15.006s user 0m0.000s sys 0m0.006s

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Apr 21, 2024

It seems this kind of case by case handling might lead to issues later.

What "issues" could that possibly be?

@tornaria
Copy link
Contributor

It seems this kind of case by case handling might lead to issues later.

What "issues" could that possibly be?

I don't know, someone wants to change something about this code, and has to be aware of the 3-4 different versions of the code. Just like #37391 was only tested on mac and seemed to work fine, having different implementations for different platforms is a risk. Since everything can e implemented with bash, we just defer to bash having the same behavior on the different platforms.

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Apr 22, 2024

having different implementations for different platforms is a risk

Based on my experience with portability work, let me just say that I don't find this believable.

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Apr 22, 2024

Since everything can be implemented with bash, we just defer to bash having the same behavior on the different platforms.

Let's check in with our most prominent antibashist (#29345, #29344, #30128) in our project. @orlitzky, any thoughts?

@tornaria
Copy link
Contributor

Note that $SECONDS can be replaced by $(date +%s) if you are concerned about that.

@orlitzky
Copy link
Contributor

Bash is fine so long as the shebang doesn't say #!/bin/sh. If bash makes this easy to fix, then I would use bash.

In case it helps, the comments to the effect that bash time can't redirect to a file aren't quite right. For example (in bash),

$ (time echo "hello world!") 2> derp
hello world!
$ cat derp

real	0m0.000s
user	0m0.000s
sys	0m0.000s

Problem is, this will redirect stderr for the inner command, too. I'm sure there's a way around it by juggling file descriptors.

@tornaria
Copy link
Contributor

Bash is fine so long as the shebang doesn't say #!/bin/sh. If bash makes this easy to fix, then I would use bash.

In case it helps, the comments to the effect that bash time can't redirect to a file aren't quite right. For example (in bash),

$ (time echo "hello world!") 2> derp
hello world!
$ cat derp

real	0m0.000s
user	0m0.000s
sys	0m0.000s

Problem is, this will redirect stderr for the inner command, too. I'm sure there's a way around it by juggling file descriptors.

Here: #37840 (comment)

@orlitzky
Copy link
Contributor

Here: #37840 (comment)

Mere inches above where I was typing, the perfect hiding place. It's not any more readable but I would bet that the bash version is more portable.

@kwankyu
Copy link
Collaborator

kwankyu commented May 10, 2024

A compromise

time_cmd() {
    exec 3>&1 4>&2
    local time=$({ time sh -c "$@" 1>&3 2>&4; } 2>& 1 )
    echo "$time" > $timefile
}

report_time() {
    local time=$(echo $(cat $timefile 2>/dev/null))
    if [[ $time =~ real\ ([0-9]+)m([0-9]+)\.[0-9]+s ]]; then
        local minutes="${BASH_REMATCH[1]}"
        local seconds="${BASH_REMATCH[2]}"
        total_seconds=$((minutes * 60 + seconds))
        if (( total_seconds >= 10 )); then
            echo "$time"
        fi
    fi
}  

Would this work?

@tornaria
Copy link
Contributor

A compromise

time_cmd() {
    exec 3>&1 4>&2
    local time=$({ time sh -c "$@" 1>&3 2>&4; } 2>& 1 )
    echo "$time" > $timefile
}

report_time() {
    local time=$(echo $(cat $timefile 2>/dev/null))
    if [[ $time =~ real\ ([0-9]+)m([0-9]+)\.[0-9]+s ]]; then
        local minutes="${BASH_REMATCH[1]}"
        local seconds="${BASH_REMATCH[2]}"
        total_seconds=$((minutes * 60 + seconds))
        if (( total_seconds >= 10 )); then
            echo "$time"
        fi
    fi
}  

Would this work?

LGTM. Two comments:

  1. if we are using bash specifics anyway, using $SECONDS to detect whether 10 seconds have elapsed seems easier and cleaner (no parsing)
  2. the reporting of time can be done inside time_cmd() itself, avoiding saving through a file, and the caller doesn't need to know about it. You just get a time_cmd which prints the time only if more than 10 seconds.

This is more or less what I proposed here:
#37840 (comment)

if you don't like the extra parameter, you can hardcode "10" there. Something like:

time_cmd() { 
    local start_seconds=$SECONDS
    exec 3>&1 4>&2
    local time=$({ time sh -c "$@" 1>&3 2>&4; } 2>& 1 )
    local total_seconds=$(($SECONDS - $start_seconds))
    if (( total_seconds >= 10 )); then
        echo "$time"
    fi
}

@kwankyu
Copy link
Collaborator

kwankyu commented May 10, 2024

  1. if we are using bash specifics anyway, using $SECONDS to detect whether 10 seconds have elapsed seems easier and cleaner (no parsing)
  2. the reporting of time can be done inside time_cmd() itself, avoiding saving through a file, and the caller doesn't need to know about it. You just get a time_cmd which prints the time only if more than 10 seconds.

My variant is a compromise, in the sense that the suggested commands can be replaced with Matthias' time_cmd() and report_time().

Anyway, this PR is Matthias's. He is free to adopt whatever we suggest or not.

@mkoeppe mkoeppe added this to the sage-10.4 milestone May 14, 2024
@mkoeppe mkoeppe changed the title sage-logger: Handle more flavors of time, not just macOS/FreeBSD sage-logger: Replace use of /usr/bin/time by bash keyword time May 16, 2024
@mkoeppe
Copy link
Contributor Author

mkoeppe commented May 16, 2024

Finally coming back to this PR. Thanks all for the suggestions. I'm preparing a version that adapts from the suggested solutions that use the bash keyword time and output redirection.

Copy link
Collaborator

@kwankyu kwankyu left a comment

Choose a reason for hiding this comment

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

Works well. LGTM. Thanks.

@mkoeppe
Copy link
Contributor Author

mkoeppe commented May 17, 2024

Thanks!

vbraun pushed a commit to vbraun/sage that referenced this pull request May 18, 2024
…sh keyword `time`

    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

Follow-up after
- sagemath#37391
- sagemath#37785

... to make the display of package build times available on Linux too.

Fixes
sagemath#37785 (comment),
closes sagemath#37832.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->
- Depends on sagemath#37785
    
URL: sagemath#37840
Reported by: Matthias Köppe
Reviewer(s): Kwankyu Lee
vbraun pushed a commit to vbraun/sage that referenced this pull request May 18, 2024
…sh keyword `time`

    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

Follow-up after
- sagemath#37391
- sagemath#37785

... to make the display of package build times available on Linux too.

Fixes
sagemath#37785 (comment),
closes sagemath#37832.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->
- Depends on sagemath#37785
    
URL: sagemath#37840
Reported by: Matthias Köppe
Reviewer(s): Kwankyu Lee
local out
TIME_OUTPUT=$((time 1>&3 2>&4 sh -c "$1") 2>&1)
local retstat=$?
[ "$SECONDS" -lt "$max" ] || echo "$TIME_OUTPUT" > "$timefile"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
[ "$SECONDS" -lt "$max" ] || echo "$TIME_OUTPUT" > "$timefile"
[ "$SECONDS" -lt "$max" ] || echo "$TIME_OUTPUT" >&2

you don't really need to go via "$timefile", do you?

It seems more natural if time_cmd has similar semantics/usage as regular time (except the time is only printed out if >= 10s).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I do. This thing runs in a subshell.

Copy link
Contributor

Choose a reason for hiding this comment

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

My point is: print to stderr instead of saving to a file. What's wrong with that? Saves a point of failure (communication via filesystem).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From my experience as the principal maintainer of the Sage distro build system, let me just say that this concern ("point of failure") is wildly beside the point.

@@ -94,8 +92,7 @@ if [ -n "$SAGE_SILENT_BUILD" -a ${use_prefix} = true ]; then
# Silent build.
# Similar to https://www.gnu.org/software/automake/manual/html_node/Automake-Silent-Rules.html#Automake-Silent-Rules
echo "[$logname] installing. Log file: $logfile"
( exec>> $logfile 2>&1 ; $TIME sh -c "$cmd"; status=$?;
[ -r $timefile ] && cat $timefile; exit $status )
( exec>> $logfile 2>&1; time_cmd "$cmd"; status=$?; report_time; exit $status )
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
( exec>> $logfile 2>&1; time_cmd "$cmd"; status=$?; report_time; exit $status )
( exec>> $logfile 2>&1; time_cmd "$cmd" )

or even maybe

Suggested change
( exec>> $logfile 2>&1; time_cmd "$cmd"; status=$?; report_time; exit $status )
time cmd "$cmd" >> $logfile 2>&$1

Also, in L104 below, the time is reported again.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One goes to the logfile. One goes to the terminal.

( exec 2>&1;
$TIME sh -c "$cmd"; status=$?; report_time;
exit $status ) | \
( exec 2>&1; time_cmd "$cmd"; status=$?; report_time; exit $status ) | \
Copy link
Contributor

Choose a reason for hiding this comment

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

same here...

time_cmd() {
local max=$(($SECONDS+10))
exec 3>&1 4>&2
local out
Copy link
Contributor

Choose a reason for hiding this comment

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

unused

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks, I'll clean this one up in a follow up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is done in #38026.

Comment on lines +70 to +71
TIME_OUTPUT=$((time 1>&3 2>&4 sh -c "$1") 2>&1)
local retstat=$?
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
TIME_OUTPUT=$((time 1>&3 2>&4 sh -c "$1") 2>&1)
local retstat=$?
local TIME_OUTPUT=$((time 1>&3 2>&4 sh -c "$1") 2>&1) retstat=$?

if you want't TIME_OUTPUT to be local, this is one way.

OTOH, if you keep TIME_OUTPUT global you can use it instead of timefile.

@kwankyu
Copy link
Collaborator

kwankyu commented May 18, 2024

@tornaria Thanks for reviewing and for additional comments. But let me express my worries about certain attitude of reviewers that I am observing recently. First my apologies for making remarks toward fellow reviewers, which I usually don't.

From our doc on "reviewing", we see

“The perfect is the enemy of the good”

The point of the review is to ensure that the Sage code guidelines are followed and that the implementation is mathematically correct. Please refrain from additional feature requests or open-ended discussion about alternative implementations. If you want the code written differently, your suggestion should be a clear and actionable request.

We develop sage "incrementally". A PR should be good enough to be accepted, but it need not be "perfect" from one reviewer's point of view. If the reviewer has an idea to improve the code further, then he can open up his own PR building on the present PR, implementing the idea.

If I have mistaken your good intention, then my apologies again.

@tornaria
Copy link
Contributor

If I have mistaken your good intention, then my apologies again.

I don't understand what you mean.

I'll make sure in the future to keep my 2c to myself.

@vbraun vbraun merged commit b13df98 into sagemath:develop May 25, 2024
21 of 22 checks passed
vbraun pushed a commit to vbraun/sage that referenced this pull request May 29, 2024
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

- Clean up after sagemath#37391, which used `sage-logger` for prefixing output;
generating the log/time files was a side effect, which has not proven to
be useful
- Fixes sagemath#37887 @jhpalmieri

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->

- Depends on sagemath#37840 (merged here)
    
URL: sagemath#38026
Reported by: Matthias Köppe
Reviewer(s): Kwankyu Lee, Matthias Köppe
vbraun pushed a commit to vbraun/sage that referenced this pull request May 31, 2024
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

- Clean up after sagemath#37391, which used `sage-logger` for prefixing output;
generating the log/time files was a side effect, which has not proven to
be useful
- Fixes sagemath#37887 @jhpalmieri

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->

- Depends on sagemath#37840 (merged here)
    
URL: sagemath#38026
Reported by: Matthias Köppe
Reviewer(s): Kwankyu Lee, Matthias Köppe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

/usr/bin/time should be a pre-req
5 participants