<div dir="ltr"><br><br><div class="gmail_quote"><div dir="ltr">On Thu, Jun 29, 2017 at 8:10 PM Xinliang David Li <<a href="mailto:davidxl@google.com">davidxl@google.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Thu, Jun 29, 2017 at 7:26 PM, David Blaikie <span dir="ltr"><<a href="mailto:dblaikie@gmail.com" target="_blank">dblaikie@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><br><br><div class="gmail_quote"><span><div dir="ltr">On Thu, Jun 29, 2017 at 7:03 PM Xinliang David Li <<a href="mailto:davidxl@google.com" target="_blank">davidxl@google.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Thu, Jun 29, 2017 at 6:27 PM, David Blaikie <span dir="ltr"><<a href="mailto:dblaikie@gmail.com" target="_blank">dblaikie@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">I haven't tested it, but it looks to me like llvm-profdata merge (well, InstrProfWriter specifically) would not have deterministic output.<br><br>Certainly the textual output iterates over FunctionData which is a StringMap of SmallDenseMaps, neither of which has deterministic iteration</div></blockquote><div><br></div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>Does the iteration order of these maps depend on the order of hashes (of strings for StringMap)?</div></div></div></div></blockquote><div><br></div></span><div>Right - that's my understanding.<br><br>(Some folks have started adding interesting things to LLVM to try to help weed out these sort of issues (ENABLE_REVERSE_ITERATION - though it's only been implemented in one of LLVM's containers so far))</div><span><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div> Anyway, text dump is for debugging purpose only.</div></div></div></div></blockquote></span><div><br>Sure, though even test cases written against this should be deterministic, but yeah - less of a concern, for sure.<br> </div><span><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">.<br><br>The binary writing looks like it'd have similar issues - looping through these unordered maps & writing output (eg: InstrProfRecordWriterTrait::EmitData loops through the data in the same SmallDenseMap and writes content in that order so far as I can tell.<br><br></div></blockquote><div><br></div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>Binary dump does not have the problem. The binary format of the indexed profile data is on-disk hashtable. Before serializing into the disk, the instProfRecord is first inserted into the in memory hashtable and the hashtable is then dumped into the disk. The entry order of the hashtable only depends on the string hashes and in case of conflicts, the function content hash. </div></div></div></div></blockquote></span><div><br>Ah, so you mean the loop in InstrProfRecordWriterTrait::EmitData that inserts into the SummaryBuilder - the SummaryBuilder itself doesn't depend on the order of 'addRecord' calls - ah, I see, addRecord -> addEntryCount -> addCount -> CountFrequencies, a std::map (thus, ordered by value, independent of call order of addRecord).<br><br>But inside that loop, it also starts writing directly to 'Out' based on the order of the SmallDenseMap, doesn't it? I don't immediately see anywhere that seeks within 'Out' to ensure that the loop there doesn't affect the ordering of the bytes written within it.<br></div></div></div></blockquote><div><br></div><div><br></div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>The instrProf records are organized in this order:</div><div><br></div><div>1) function name hashing order;</div><div>2) for two functions with identical name/name hash, the hashing order of the function CFG hash.</div><div><br></div><div>The loop in EmitData is only for 2). It does depend on the SmallDenseMap's entry order (with hash key being uint64). For majority of the cases, the map has only 1 entry (note the string uses MD5 checksum for hashing, so the collision rate is very low. For static functions, the compiler will add module id to differentiate. For practical purpose, we can consider the map is almost always size 1. In rare cases, it may contain > 1 (e.g, a small number such as 2) entries, do you see the possibility of their orders to be different from run to run (i.e, depending on memory address)? It seems unlikely, but I have not checked the map's implementation in details to be sure.</div></div></div></div></blockquote><div><br>Ah, sorry for the distraction - realized I conflated a few things.<br><br>Generally the contract for hashing containers is that they have no ordering guarantees. This means even that they may change run-over-run, for the same inputs (not even pointers where the elements change run over run due to memory layout, etc).<br><br>But this applies perhaps more firmly to the standard library - LLVM should be portable & deterministic between different standard library implementations (so any dependence on the ordering of std::unordered_map would be undesirable).<br><br>Dependence on the ordering of LLVM's internal containers is less of an issue - since it doesn't create portability concerns, just a minor maintenance cost if these containers are modified/improved/etc.<br><br>I'm not sure anyone's interested in making LLVM independent of changes to its own internal containers hash ordering, nor how close the project is today.<br><br>So I'll leave this bit alone for now - thanks for talking it through with me (:<br><br>- Dave <br><br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><div>David</div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div> </div><span><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>If you do see case of non-determinism, then we have a bug there which should be fixed, but there is no need to change the iteration order of the IntrProfWriter. </div></div></div></div></blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div></div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><br>Generally it's important that the compiler (& I believe related tools) have deterministic output. Is there a reason that wouldn't be the case for llvm-profdata? Or have I misunderstood how the output is determined?<br><br></div></blockquote><div><br></div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>For the binary output, we definitely need deterministic behavior. For debug output, we can relax that requirement.</div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><br>Ensuring deterministic output may be expensive in terms of memory usage, though perhaps not prohibitive. The usual approach is to use some of LLVM's deterministic maps (like MapVector), though they're not exactly tuned for memory usage. An alternative might be to take the data in each SmallDenseMap and sort it by the hash as a key - it's unique after all, and doing each map separately won't do crazy bad things to memory usage (a small constant overhead).<br><br>Handling the StringMap, I'm not sure about - it might be cheap enough to make a separate vector of StringMapEntry*s, sorting based on the strings and iterating over that instead of the StringMap itself? (I guess the same approach could be taken with the SmallDenseMaps, rather than duplicating anything)<br><br>How's all that sound?</div></blockquote><div><br></div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>See above -- I don't think we have a need to change the use of StringMap. If we see a case where the non-determinism happens, we need to root-cause it first.</div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><div>David</div><div><br></div><div> </div></div></div></div></blockquote></span></div></div>
</blockquote></div></div></div></blockquote></div></div>