Skip to content
New issue

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

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

Already on GitHub? # to your account

SurfaceWindow struct refactoring #8141

Merged
merged 62 commits into from
Sep 2, 2020
Merged

Conversation

xuanluo113
Copy link
Contributor

@xuanluo113 xuanluo113 commented Jul 8, 2020

Pull request overview

The PR includes several refactoring effort on the SurfaceWindow related data structures, including:

  • Convert the SurfaceWindow object arrays to a list of scalar arrays (src/EnergyPlus/DataSurfaces.cc). This improves cache locality.
  • Reduce the scope of several Surface loops to ZoneSurface loops, nested with a SurfOpaq loop and a SurfWin loop separately, moving the branches out of the loop to help vectorization.
  • Rename the surface window scalar arrays in src/EnergyPlus/HeatBalanceSurfaceManager.cc by adding a prefix - "SurfWin" to be consistent with the SurfaceWindow naming conventions. This is to benefit follow-up efforts on continuing to reduce the scope of window-only local arrays, loops, and functionalities.
  • Convert the timestep reinitialization of Surface and SurfWin arrays from the array-wise assignment (Array1D A = 0.0) to the element-wise assignment. This helps compiler level optimization.

Pull Request Author

Add to this list or remove from it as applicable. This is a simple templated set of guidelines.

  • Title of PR should be user-synopsis style (clearly understandable in a standalone changelog context)
  • Label the PR with at least one of: Defect, Refactoring, NewFeature, Performance, and/or DoNoPublish
  • Pull requests that impact EnergyPlus code must also include unit tests to cover enhancement or defect repair
  • Author should provide a "walkthrough" of relevant code changes using a GitHub code review comment process
  • If any diffs are expected, author must demonstrate they are justified using plots and descriptions
  • If changes fix a defect, the fix should be demonstrated in plots and descriptions
  • If any defect files are updated to a more recent version, upload new versions here or on DevSupport
  • If IDD requires transition, transition source, rules, ExpandObjects, and IDFs must be updated, and add IDDChange label
  • If structural output changes, add to output rules file and add OutputChange label
  • If adding/removing any LaTeX docs or figures, update that document's CMakeLists file dependencies

Reviewer

This will not be exhaustively relevant to every PR.

  • Perform a Code Review on GitHub
  • If branch is behind develop, merge develop and build locally to check for side effects of the merge
  • If defect, verify by running develop branch and reproducing defect, then running PR and reproducing fix
  • If feature, test running new feature, try creative ways to break it
  • CI status: all green or justified
  • Check that performance is not impacted (CI Linux results include performance check)
  • Run Unit Test(s) locally
  • Check any new function arguments for performance impacts
  • Verify IDF naming conventions and styles, memos and notes and defaults
  • If new idf included, locally check the err file and other outputs

@xuanluo113 xuanluo113 added Performance Includes code changes that are directed at improving the runtime performance of EnergyPlus InProgress Changes to address this feature request have been started labels Jul 8, 2020
@xuanluo113 xuanluo113 self-assigned this Jul 8, 2020
@mjwitte mjwitte added this to the EnergyPlus 9.4.0 milestone Aug 28, 2020
@xuanluo113 xuanluo113 changed the title Scalar SurfaceWindow struct SurfaceWindow struct refactoring Sep 1, 2020
@xuanluo113 xuanluo113 marked this pull request as ready for review September 1, 2020 16:50
@xuanluo113
Copy link
Contributor Author

@mjwitte @Myoldmopar This branch is ready for review and the refactoring efforts included in the current commits are listed in the PR overview. The CI is clean and CI performance suite is showing a 1.89% speedup.
Some other surface arrays refactoring efforts are ongoing, but merging is getting harder so I clean up some solid features and hope to get them reviewed and merged first. I'll start another branch/draft PR to work on it and keep this one as it is. Please let me know if you suggest otherwise.
@mjwitte If you want to start your investigation on movable insulation on top of this, please refer to this branch.

@amirroth
Copy link
Collaborator

amirroth commented Sep 1, 2020 via email

@xuanluo113 xuanluo113 removed the request for review from amirroth September 1, 2020 17:39
Copy link
Member

@Myoldmopar Myoldmopar left a comment

Choose a reason for hiding this comment

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

I'll build locally with develop one more time and run tests.

@@ -1548,6 +1547,7 @@ namespace AirflowNetworkBalanceManager {
using DataLoopNode::NodeConnectionType_Inlet;
using DataLoopNode::NodeType_Air;
using DataLoopNode::ObjectIsParent;
using DataSurfaces::SurfWinOriginalClass;
Copy link
Member

Choose a reason for hiding this comment

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

Adding more usings isn't great. Probably not worth more commits, but we are moving away from it. Even if it makes the lines of code a little wider...that's hardly a problem on most folks monitors these days.

@@ -5153,19 +5153,19 @@ namespace AirflowNetworkBalanceManager {
}
SetupOutputVariable("AFN Surface Venting Window or Door Opening Modulation Multiplier",
OutputProcessor::Unit::None,
SurfaceWindow(SurfNum).VentingOpenFactorMultRep,
DataSurfaces::SurfWinVentingOpenFactorMultRep(SurfNum),
Copy link
Member

Choose a reason for hiding this comment

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

And in some cases, you put the namespace to avoid the using.

// store the magnitude and time of the pulse
radiantPulseTimestep(CurOverallSimDay, NZ) = (HourOfDay - 1) * NumOfTimeStepInHour + TimeStep;
radiantPulseReceived(CurOverallSimDay, SurfNum) = (adjQL - curQL) * TMULT(radEnclosureNum) * ITABSF(SurfNum) * Surface(SurfNum).Area;
for (int zoneNum = 1; zoneNum <= NumOfZones; ++zoneNum) {// Loop through all surfaces...
Copy link
Member

Choose a reason for hiding this comment

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

Alright, I'm glad CI is showing no diffs to help me feel better about this functional change.

@@ -5515,7 +5513,7 @@ namespace InternalHeatGains {
}

for (Loop = 1; Loop <= TotCO2Gen; ++Loop) {
NZ = ZoneCO2Gen(Loop).ZonePtr;
int NZ = ZoneCO2Gen(Loop).ZonePtr;
Copy link
Member

Choose a reason for hiding this comment

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

Localizing NZ is great.


std::vector<int> AllHTSurfaceList; // List of all heat transfer surfaces
std::vector<int> AllIZSurfaceList; // List of all interzone heat transfer surfaces
std::vector<int> AllHTNonWindowSurfaceList; // List of all non-window heat transfer surfaces
std::vector<int> AllHTWindowSurfaceList; // List of all window surfaces
std::vector<int> AllSurfaceListReportOrder; // List of all surfaces - output reporting order

// Surface Window Heat Balance
Array1D<Real64> SurfWinTransSolar; // Exterior beam plus diffuse solar transmitted through window, or window plus shade/blind, into zone (W)
Copy link
Member

Choose a reason for hiding this comment

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

This is a lot. I know this is one of the parts of EnergyPlus where things are looped over quickly, so I concede that this provides a performance benefit, but just no one get any ideas about doing this in lots of places in the code where we don't do tight loops over array items.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't quite get the sentence after "but". By "doing this in lots of places in the code where we don't do tight loops over array items " do you mean using scalar arrays instead of struct arrays in places with loops that only call a few fields in the original SurfaceWindow struct?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, what is "this" in "this is a lot" referring to?

Copy link
Collaborator

Choose a reason for hiding this comment

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

If what you are referring to is the change from arrays of structs to arrays of scalars, then I don't think it's only a matter of tight loops. Tight loops (i.e., loops with no function calls or conditionals) are what you want for vectorization. But even without vectorization (which I don't think we are getting now) switching to arrays of scalars will improve your cache locality and utilization. If you have structs with a lot of fields that are only used in some calculations, in a given calculation you are probably bringing in a lot of that data unnecessarily. I think this is primarily what we are seeing here. I don't know that this type of transformation (which is really only partial for now) would be effective elsewhere in the code, but I would not dismiss it out of hand.

@Myoldmopar
Copy link
Member

This runs fine locally. I'll leave this tab open to respond to comments but I have to run for now.

@Myoldmopar Myoldmopar merged commit c3fbac0 into NREL:develop Sep 2, 2020
@Myoldmopar Myoldmopar deleted the window-test branch September 2, 2020 21:42
Copy link
Contributor

@mjwitte mjwitte left a comment

Choose a reason for hiding this comment

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

@xuanluo113 Some initial comments.

@JasonGlazer This is likely to conflict with #8196 but oddly not as much as I thought, because many of the fields related to shading (e.g. WindowShadingControlPtr) live on Surface and SurfaceTemp, not on SurfaceWindow. But ShadedConstruction exists in both places - ugh. I'm not finding where SurfWinShadedConstruction(SurfNum) gets set to Surface(SurfNum).ShadedConstruction.

But this didn't throw diffs and it just merged, so . . .

for (int zoneNum = 1; zoneNum <= NumOfZones; ++zoneNum) {
int const firstSurfWin = Zone(zoneNum).WindowSurfaceFirst;
int const lastSurfWin = Zone(zoneNum).WindowSurfaceLast;
if (firstSurfWin == -1) continue;
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, these checks shouldn't be necessary. WindowSurfaceFirst is initialized to zero, and for zones with no windows, WindowSurfaceLast is set to -1 any loops on windowsurfaces will be skipped over. In fact, looking at this now, WindowSurfaceLast and NonWindowSurfaceLast should simply be initialized to -1.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Relying on windowSurfaceFirst to be initialized to 0 and windowSurfaceLast to be initialized to -1 is juuuuuuuust a little too cute. It's a recipe for bugs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mjwitte I somehow wrongly pre-assumed this. And -1 is preferable if we want to convert these to vectors eventually? Can I make a follow-up PR to set WindowSurfaceLast and NonWindowSurfaceLast to -1 when no windows/opaqs and revised the checking?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Whatever you do, I would keep the check. Don't get too cute with loop bounds. It's not worth it.

Copy link
Contributor

Choose a reason for hiding this comment

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

@amirroth As it stand presently, *First and *Last are currently initialized to zero, and then *Last is set to -1 if there are no surfaces in that group. Is there a better way to skip those loops when they are empty?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, set them both to -1 by default (which works for both 1- and 0-based arrays) and do a separate check. A check before a loop whether to do the loop or not is fine, in fact the compiler often inserts checks like that itself. A check on every element in the loop is a lot less fine.

Comment on lines -431 to -434
Array1D<Real64> WinTransSolar; // Exterior beam plus diffuse solar transmitted through window, or
// window plus shade/blind, into zone (W)
Array1D<Real64> WinBmSolar; // Exterior beam solar transmitted through window, or
// window plus blind, into zone (W)
Copy link
Contributor

Choose a reason for hiding this comment

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

Wondering why you renamed arrays that were not previously part of SurfaceWindow?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the naming convention/prefix we discussed about. SurfWin, SurfOpaq, Surf, and Zone to help understand their scope?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Like, anything starts with SurfWin is expected to be window indexed only and inside firstWindowSurf to lastWindowSurf loop.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I remember that discussion. I just didn't understand the scope of that went beyond the original SurfaceWindow struct. Not saying it needs to be undone, just didn't expect that.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This convention should be propagated everywhere. The current naming convention "scheme" is terrible. As someone who doesn't actually know what should be going on, let me tell you, it really makes the code difficult to read.

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 remember that discussion. I just didn't understand the scope of that went beyond the original SurfaceWindow struct. Not saying it needs to be undone, just didn't expect that.

:sweat_smile Yes. That went beyond that. Just after converting 30 of those beyond SurfaceWindow, I realized there are another 300 of them that better get renamed and I decided to get this partial effort merged before getting too far.

@xuanluo113
Copy link
Contributor Author

@mjwitte It's set here. Perhaps only Surface(SurfNum).ShadedConstruction or SurfShadedConstruction(SurfNum) is needed, but what happened here was an automated conversion.

@mjwitte
Copy link
Contributor

mjwitte commented Sep 2, 2020

I see. @JasonGlazer Not sure if you've allowed for multiple blind numbers in #8196 . See here - this block looks like it may need modification to allow for multiple windowshadingcontrols.

@JasonGlazer
Copy link
Contributor

@mjwitte I will take a look at the code you pointed out.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
InProgress Changes to address this feature request have been started Performance Includes code changes that are directed at improving the runtime performance of EnergyPlus
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants