profiler
profiler copied to clipboard
WIP - Add first pass at adding carbon metrics to Firefox profiler
Hi there,
This is a veeeeeery early version of a PR for adding carbon metrics into the Firefox profiler view, largely as an experiment, and to see if adding carbon emission numbers into dev tools for browsers is even plausible.
It's based on the recent introduction of higher resolution power metrics added into Firefox as of 104, and some recent work we've been doing on the CO2.js library to make carbon emissions figures easy to work with if you have energy usage figures available.
The new, more detailed energy figures make it possible to surface some carbon metrics numbers in Firefox profiling, because in many cases, if you're not trying to track changes in carbon intensity that happen from hour to hour, you can use annual conversion factors to turn electricity usage figures in kilowatt hours (or similar units like megawatt hours, watt hours or milliwatt hours) to carbon emissions figures in terms of grams or carbon dioxide equivalent.
So basically, on a profile run like this:

I've extended this to allow for carbon tracking like so. Because most carbon figures are expressed as in terms of carbon dioxide equivalent, rather than a rate, for the time being this only shows the figure for either the visible range in the profiler, or the selection:

Feedback I'm looking for
I'm really looking for any real review yet - I just want to give a heads up that I'm looking at this, and to find some time in October to ask a few more questions about it.
Code style feedback
I've only ever used React for a few hours at a Mozilla hackday a few years back, so I apologise in advance the copy pasta in this code. I'd welcome any pointers once I've had a chance to tidy this.
This PR in its current state is dependent on a new release of CO2.js that goes out the door next week - we'll include carbon intensity figures from both Ember Climate a European think tank that has been creating open datasets, and also marginal carbon intensity figures from the UNFCCC, which the Green Web Foundation is publishing as open data, and would make easier to carbon software carbon intensity scores as described by the Green Software Foundation.
A note on the numbers
Please don't take any of the carbon numbers seriously yet - I haven't paid any real attention to getting them right yet, as I only found out how the profiler works last night from watching videos on youtube, and looking at accepted PRs, and I haven't dedicated any time to making sure I'm even using the correct units - I just know that if you have the electricty usage figures, you can start to put together some indicate carbon numbers.
TODO
- [ ] Add tests
- [ ] Add actual carbon figures from CO2.js
- [ ] Figure out how to provide more localised numbers rather than the global constant
- [ ] Refactor code so we're not just blindly copying from ideas in PRs
Hmm, do we need a new track for this? It displays the same graph as the Process Power track. Could this information just be added to the Process Power track's tooltip?
I meant to comment here, sorry for the delay.
Here is my understanding: The current implementation is that the CO2 track is simply a factor of the power track. My understanding is that in the future we'll want to use the CO2 library to fetch information about the real factor to use. My understanding is also that this factor would be the same for a whole session of a profile (which is only a few seconds, possibly minutes, long), so indeed like Markus I think that a separate track is probably not so useful.
Therefore there are IMO several consequences:
- fetching information => would that be at capture time or at reading time? In both cases there are privacy concerns, in that we'll fetch data from an external service giving away a location.
- which location are we interested in? The one where it was captured? In that case should we store it in the profile (more privacy concerns possibly, even if approximate)? Or should we store the factor only (probably a better idea, but would it be still possible to find the location from the factor)?
- Would it be possible to store CO2 information statically in the profiler code instead of fetching that information? Is that a lot of data? We could update it regularly, and use it at capture time to know the factor and store the factor only in the processed format.
- or we can let the user decide the location to compute CO2 information from -- in that case we don't need to fetch or store anything sensitive, however this needs a user action.
Here are the few comments and questions I can think of, please add more if you see other concerns :-) Or answers of course!
@mstange do you mean something like this:

I'll check with @mrchrisadams later this to sanity check my understanding of the carbon calculations. If I'm reading it right, you're generating a power usage value in picowatts which is then later converted to watts & milliwatts for display in the track. Is that right?
We plan to use thegreenwebfoundation/co2.js to calculate the carbon estimates. There we're using gCO2e per kWh, so there might need to be another conversion step.
I've got a working version used to generate the screenshot above based off https://github.com/thegreenwebfoundation/profiler/tree/ca-carbon-metrics but won't commit anything until I've spoken with Chris later this week.
@julienw saw your comment after posting mine above. To address some of your concerns out of order:
- It should be possible to integrate the CO2.js library (and associated emissions figures) into the profiler itself. We publish to NPM, or you can compile it yourself. There'd be no need to make an external call for any of the data.
- The carbon intensity figures we would be referring to are at the country-level. A minified version of this essentially. So, if grid intensity factor for was to be stored inside of the profile, you should only be able to work out the country in which that profile was taken.
- which location are we interested in? The one where it was captured? yes I think this makes sense. So we would need a way for the profiler to be aware of the user's location which might require opt-in (??) / raise a privacy risk (??)
Just thinking about the third point a bit more - as a fallback (should there be legitimate privacy risks we can't otherwise mitigate) we could have a way through a settings option where users can select a location for the profile they are capturing.
I've taken an initial go at adding CO2.js into the profiler & using global average intensity data to output a CO2e figure. Since the average intensity figures provided in CO2.js are in kWh (kilowatt-hours) I've made the conversion from picowatt-hours before multiplying by the intensity (link to line). Unsure if this is the right way to go about it, so definitely welcome guidance.
hey @fershad thanks for adding the intensity figures.
@julienw - I've tried answering the questions below:
- fetching information => would that be at capture time or at reading time? In both cases there are privacy concerns, in that we'll fetch data from an external service giving away a location.
there would be no network requests made with this library when looking up carbon intensity - I guess you fetch the original dataset when you download / update firefox. The code would checks against a local dataset, that is based on data that updated annually. I think the the underlying data, especially the average intensity figures from Ember Climate may start being updated more frequently. If it was, we'd like to be able to update the data we make visible, but it's not a thing we've explicitly promised in any roadmap.
- which location are we interested in? The one where it was captured? In that case should we store it in the profile (more privacy concerns possibly, even if approximate)? Or should we store the factor only (probably a better idea, but would it be still possible to find the location from the factor)?
I would assume you'd care about where it was captured. However, because it's just a currently a simple conversion factor, as long as you have the energy readings, it's something you could likely change or override from a default.
You might do this if the numbers were incorrect or not as precise as you want it to be. For example, there may be cases where you are not relying on energy from the grid. In other scenarios the country average may be wiser to use, (in America, different states have different intensity figures, but we currently do not try to represent that).
- Would it be possible to store CO2 information statically in the profiler code instead of fetching that information? Is that a lot of data? We could update it regularly, and use it at capture time to know the factor and store the factor only in the processed format.
Yes, this is the approach we are proposing in this PR.
Depending on where you are in the world, the carbon intensity can fluctuate based on the time of day, or location.
There are APIs you can query to fetch this higher resolution info, but:
- they almost always require API keys and registration
- they often cost money
- they rely on making HTTPS network requests, where you send information to identify the client requesting the data
It would be nice to be able to do this, and we have libraries that do wrap some these APIs, but I figured anything that makes network requests under these conditions would be outside the scope of this PR.
- or we can let the user decide the location to compute CO2 information from -- in that case we don't need to fetch or store anything sensitive, however this needs a user action.
In a related project, we took the following approach:
1 . try to guess based on the computers locale information 2. try to make it obvious where we got this information, as the locale was not always accurate, and allow the user to override it
The approach here currently assumes a global average. I don't know the best place to add options to switch locations to use a different country level carbon intensity, and any pointers would be gratefully received.
We sketched out on approach for using navigator.geolocation.getCurrentPosition in this notebook on nextjournal when experimenting with various APIs, but as with the previous question, it feels like it's extending the scope quite a lot and starting with a small PR might be smart.
@flodolo thank you for the feedback. I've applied those changes.
To summarise where we are now:
- We have included carbon intensity figures in a tooltip alongside the respective power figures.
- We are using global average grid intensity data from https://github.com/thegreenwebfoundation/co2.js to calculate carbon emissions based on power usage.
- First convert picowatt-hours to kilowatt-hours
- Then multiple by the global average grid intensity
- We have tooltip text in
en_USat the moment.
Next steps
We'll need some (more) hand holding/leading by the Firefox folks now. In terms of next steps, here's what we have thought of internally:
- How do we go about getting the tooltip strings localised?
- Are carbon figures something that should be shown by default, or would it be preferable to have them behind a flag for now?
- We are currently using the global average grid intensity. Ideally we would be using local (country-level) average grid intensity where available. There were also some concerns raised about privacy in comments above. How do we go about progressing here?
I just tried the deploy preview (https://deploy-preview-4243--perf-html.netlify.app/public/n6vyjpmv615vhf7zrhy6rbczvenqb4nj2zjf1mg/marker-chart/?globalTrackOrder=ab0w9&hiddenGlobalTracks=1wb&hiddenLocalTracksByPid=11073-0wa~11082-0w2~11074-0w2~11080-0w2~11084-0w2~11083-0w2&range=7958m1320&thread=0&v=7). This is exciting!
I find that the strings are pretty long and make the tooltip harder to read.
How would you feel about instead showing the data like this?
Energy used in the current selection: 0.087 mWh (0.039 mg CO2e)
Energy used in the visible range: 0.27 mWh (0.12 mg CO2e)
It's less descriptive, but I think anybody googling "CO2e" will find the meaning if they don't know it yet.
And I think this answers your question about whether we can show these figures by default: if it gets in the way we will need to hide them by default. If it doesn't get in the way, I think we should totally show them by default.
@fqueze this change makes sense. We've spoken about this internally, and feel that it is the right direction. How do we go about updating the strings to represent this?
How do we go about updating the strings to represent this?
I think @julienw can help you with the next steps.
I took a crack at this, and think that I've got it working. Would appreciate a review of 4d24bfb to make sure I've not done anything too hacky.

Hi @julienw, are you able to guide us as to next steps?
Hey @fershad, sorry this fell through the cracks, I'll try to give some more feedback soon.
Thank @julienw. I've made updates based on your feedback. It looks like there are some merge conflicts that I'll need some help to resolve.
@julienw I messed up something along the rebase path & ended up in Git purgatory. I synced our fork with main and reapplied our changes again. I've covered off your two comments above too.
I've kept with milligrams (mg) for the microwatts (µWh) co2e figure. I find this is easier to parse, and keeps consistency with the other values shown (at least when I tested locally).
@mrchrisadams @fershad Can you please make sure that you checked this checkbox (it should be at the bottom of the right sidebar)?

This would allow me to easily do some small changes before merging :-)
hi @julienw, I've looked, but I can't seem to find the option to allow edits by maintainers as you have requested.
I think it might be related to an issue on github itself, based on the link below - I'm not sure you can make editable PRs from one organisation to another, only from an individual account. I think either me or Fershad would need to:
- make a new individual fork
- cherry pick the commits from the org fork
- create a new pull request, granting edit rights.
I'm basing this on the documentation below:
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork
ah I believe you're right, thanks for checking, no worries then, I'll create a separate PR and merge then. Just waiting for @flodolo to give his green light first :-)
This is merging in with #4372, so I'm closing this PR. Of course I retained the author information for the commits, but I merged some together so that the commits look more like logical pieces of work.
Thanks for this work!