<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Wed, Sep 6, 2017 at 3:58 PM, Chema Casanova <span dir="ltr"><<a href="mailto:jmcasanova@igalia.com" target="_blank">jmcasanova@igalia.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi Connor and Curro,<br>
<div><div class="h5"><br>
On 28/08/17 12:24, Alejandro Piñeiro wrote:<br>
> On 27/08/17 20:24, Connor Abbott wrote:<br>
>> Hi,<br>
>><br>
>> On Aug 25, 2017 9:28 AM, "Alejandro Piñeiro" <<a href="mailto:apinheiro@igalia.com">apinheiro@igalia.com</a><br>
>> <mailto:<a href="mailto:apinheiro@igalia.com">apinheiro@igalia.com</a>>> wrote:<br>
>><br>
>>     On 24/08/17 21:07, Connor Abbott wrote:<br>
>>     ><br>
>>     > Hi Alejandro,<br>
>><br>
>>     Hi Connor,<br>
>><br>
>>     ><br>
>>     > This seems really suspicious. If the live ranges are really<br>
>>     > independent, then the register allocator should be able to<br>
>>     assign the<br>
>>     > two virtual registers to the same physical register if it needs to.<br>
>><br>
>>     Yes, it is true, the register allocator should be able to assign two<br>
>>     virtual registers to the same physical register. But that is done<br>
>>     at the<br>
>>     end (or really near the end), so late for the problem this<br>
>>     optimization<br>
>>     is trying to fix.<br>
>><br>
>><br>
>> Well, my understanding is that the problem is long compilation times<br>
>> due to spilling and our not-so-great implementation of it. So no,<br>
>> register allocation is not late for the problem. As both Curro and I<br>
>> explained, the change by itself can only pessimise register<br>
>> allocation, so if it helps then it must be due to a bug in the<br>
>> register allocator or a problem in a subsequent pass that's getting<br>
>> hidden by this one.<br>
><br>
> Ok.<br>
><br>
>><br>
>>     We are also reducing the amount of instructions used.<br>
>><br>
>><br>
>> The comments in the source code say otherwise. Any instructions<br>
>> eliminated were from spilling, which this pass only accidentally reduces.<br>
><br>
> Yes, sorry, I explained myself poorly. The optimization itself doesn't<br>
> remove any instructions. But using it reduces the final number of<br>
> instructions, although as you say, they are likely due reducing the<br>
> spilling.<br>
><br>
>><br>
>><br>
>><br>
>>     Probably not really clear on the commit message. When I say<br>
>>     "reduce the<br>
>>     pressure of the register allocator" I mean having a code that the<br>
>>     register allocator would be able to handle without using too much<br>
>>     time.<br>
>>     The problem this optimization tries to solve is that for some 16<br>
>>     bit CTS<br>
>>     tests (some with matrices and geometry shaders), the amount of virtual<br>
>>     registers used and instructions was really big. For the record,<br>
>>     initially, some tests needed 24 min just to compile. Right now, thanks<br>
>>     to other optimizations, the slower test without this optimization<br>
>>     needs<br>
>>     1min 30 seconds. Adding some hacky timestamps, the time used  at<br>
>>     fs_visitor::allocate_registers (brw_fs.cpp:6096) is:<br>
>><br>
>>     * While trying to schedule using the three available pre mode<br>
>>     heuristics: 7 seconds<br>
>>     * Allocation with spilling: 63 seconds<br>
>>     * Final schedule using SCHEDULE_POST: 19 seconds<br>
>><br>
>>     With this optimization, the total time goes down to 14 seconds (10<br>
>>     + 0 +<br>
>>     3 on the previous bullet point list).<br>
>><br>
>>     One could argue that 1min 30 seconds is okish. But taking into account<br>
>>     that it goes down to 14 seconds, even with some caveats (see below), I<br>
>>     still think that it is worth to use the optimization.<br>
>><br>
>>     And a final comment. For that same test, this is the final stats<br>
>>     (using<br>
>>     INTEL_DEBUG):<br>
>><br>
>>      * With the optimization: SIMD8 shader: 4610 instructions. 0 loops.<br>
>>     130320 cycles. 15:9 spills:fills.<br>
>>      * Without the optimization: SIMD8 shader: 12312 instructions. 0<br>
>>     loops.<br>
>>     174816 cycles. 751:1851 spills:fills.<br>
>><br>
>><br>
>> So, the fact that it helps at all with SIMD8 shows that my theory is<br>
>> wrong, but since your pass reduces spilling, it clearly must be<br>
>> avoiding a bug somewhere else. You need to compare the IR for a shader<br>
>> with the problem with and without this pass right before register<br>
>> allocation. Maybe the sources and destinations of the conversion<br>
>> instructions interfere without the change due to some other pass<br>
>> that's increasing register pressure, in which case that's the problem,<br>
>> but I doubt it.<br>
><br>
> Ok, thanks for the hints.<br>
<br>
</div></div>After some research we found that we need to adapt the live_variables<br>
algorithm to support 32 to 16-bit conversions. Because of the HW<br>
alignment restrictions these conversions need that the result register<br>
uses stride=2, so it is not continuous (stride!=1) so by definition<br>
is_partial_write returns true. Any of the next last 3 conditions could<br>
be true when we use 16-bit types.<br>
<br>
bool<br>
fs_inst::is_partial_write() const<br>
{<br>
   return ((this->predicate && this->opcode != BRW_OPCODE_SEL) ||<br>
           (this->exec_size * type_sz(this->dst.type)) < 32 ||<br>
           !this->dst.is_contiguous() ||<br>
           this->dst.offset % REG_SIZE != 0);<br>
}<br>
<br>
So at the check on the setup_one_write function at<br>
brw_fs_live_variables.cpp the variable isn't marked as defined<br>
completely in the block.<br>
<br>
   if (inst->dst.file == VGRF && !inst->is_partial_write()) {<br>
      if (!BITSET_TEST(bd->use, var))<br>
         BITSET_SET(bd->def, var);<br>
   }<br>
<br>
That makes that the live start of the variable is expected to defined<br>
before the beginning of the block. In the commented test by Alejandro we<br>
have a big unrolled loop that increases the pressure of the register<br>
artificially making all result registers of a conversion to start life<br>
at instruction 0.<br></blockquote><div><br></div><div>Quick drive-by comment.  Curro and I talked about this quite a bit in the office yesterday as I've been hitting similar issues with the vec2->double conversion that we do when loading 64-bit values from UBOs and SSBOs.  He's got a patch to liveness which makes registers which are only ever partially written not extned their live ranges infinitely upward.  That's probably easier than trying to track things per-component.</div><div><br></div><div>--Jason<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
A simpler approach to the submitted optimization would be to consider as<br>
a fully defined variable in a block the result of a 32-16 bit<br>
conversion. This way the register allocator has more freedom to assign<br>
the register because the live_interval is correct for this variable.<br>
Something like the following code:<br>
<br>
diff --git a/src/intel/compiler/brw_fs_<wbr>live_variables.cpp<br>
b/src/intel/compiler/brw_fs_<wbr>live_variables.cpp<br>
index c449672a51..98d8583eaa 100644<br>
--- a/src/intel/compiler/brw_fs_<wbr>live_variables.cpp<br>
+++ b/src/intel/compiler/brw_fs_<wbr>live_variables.cpp<br>
@@ -83,7 +83,10 @@ fs_live_variables::setup_one_<wbr>write(struct block_data<br>
*bd, fs_inst *inst,<br>
    /* The def[] bitset marks when an initialization in a block completely<br>
     * screens off previous updates of that variable (VGRF channel).<br>
     */<br>
-   if (inst->dst.file == VGRF && !inst->is_partial_write()) {<br>
+   if (inst->dst.file == VGRF && (!inst->is_partial_write() ||<br>
+                                  ((type_sz(inst->dst.type) == 2) &&<br>
+                                   (inst->opcode == BRW_OPCODE_MOV) &&<br>
+                                   (type_sz(inst->src[0].type) == 4)))) {<br>
       if (!BITSET_TEST(bd->use, var))<br>
          BITSET_SET(bd->def, var);<br>
    }<br>
<br>
This new code reduces the spills from 1217 to 1089 in our worse tests<br>
compared to using the submitted optimization and without regressions.<br>
<br>
Although this is a simple fix that address the problem with the<br>
conversions (that is the main use that 16bit arithmetic of the<br>
VK_KHR_16bit_storage extension), there are other cases that we didn't<br>
addressed with the reuse_16bit_conversions_<wbr>register that need a more<br>
complex approach.<br>
<br>
I'm thinking about the cases when 16-bit types are packed/unpacked or<br>
shuffled/unshuffled in the storage operations. They are also<br>
partial_writes but the aggregation of several operations make that the<br>
register could be considered as completely defined in a block.<br>
<br>
- This would need doing a "subregister" tracking for stride=2 and<br>
offset=[0,2] for shuffled components that are written using 32-bit<br>
storage operations.<br>
- And the same for packet 16-bit values on SIMD8 that use half register<br>
continuously.<br>
<br>
So if only half of the register is defined for a given variable and only<br>
that half is read by the shader we can consider consistent that the<br>
variable is completely defined. But in the case that the different<br>
halves are defined in different blocks things get complicated but could<br>
stay as partial write.<br>
<br>
Does it makes sense to go deeper with an approach like this one? Maybe<br>
is not strictly needed for this series but it could be an interesting<br>
follow up for adjusting the liveness of the payloads of the 16-bit store<br>
operations and any future use of 16-bit types.<br>
<br>
Thanks in advance for your feedback.<br>
<span class="HOEnZb"><font color="#888888"><br>
Chema<br>
</font></span><span class="im HOEnZb"><br>
>> (IIRC there's a debug option to print out the register pressure for<br>
>> each instruction, which will be helpful here).<br>
><br>
> I tried to use that option back when I was working on this patch,<br>
> without too much luck. Will try again.<br>
><br>
>> If that's not the case, you should check to see if the register<br>
>> allocator thinks the sources and destinations of the conversion<br>
>> instructions interfere, and if so figure out why - that will likely be<br>
>> the real bug.<br>
><br>
> Ok.<br>
><br>
> Thanks Connor and Curro for the comments. I will work on a different<br>
> solution.<br>
<br>
</span><div class="HOEnZb"><div class="h5">______________________________<wbr>_________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev" rel="noreferrer" target="_blank">https://lists.freedesktop.org/<wbr>mailman/listinfo/mesa-dev</a><br>
</div></div></blockquote></div><br></div></div>