On Thu, 24 Sep 2026 18:11:09 GMT, Andrew Haley <[email protected]> wrote:
>> Please use [this >> link](https://github.com/openjdk/jdk/pull/28541/changes?w=1) to view the >> files changed. >> >> Profile counters scale very badly. >> >> The overhead for profiled code isn't too bad with one thread, but as the >> thread count increases, things go wrong very quickly. >> >> For example, here's a benchmark from the OpenJDK test suite, run at >> TieredLevel 3 with one thread, then three threads: >> >> >> Benchmark (randomized) Mode Cnt Score Error Units >> InterfaceCalls.test2ndInt5Types false avgt 4 27.468 ± 2.631 ns/op >> InterfaceCalls.test2ndInt5Types false avgt 4 240.010 ± 6.329 ns/op >> >> >> This slowdown is caused by high memory contention on the profile counters. >> Not only is this slow, but it can also lose profile counts. >> >> This patch is for C1 only. It'd be easy to randomize C1 counters as well in >> another PR, if anyone thinks it's worth doing. >> >> One other thing to note is that randomized profile counters degrade very >> badly with small decimation ratios. For example, using a ratio of 2 with >> `-XX:ProfileCaptureRatio=2` with a single thread results in >> >> >> Benchmark (randomized) Mode Cnt Score Error >> Units >> InterfaceCalls.test2ndInt5Types false avgt 4 80.147 ± 9.991 >> ns/op >> >> >> The problem is that the branch prediction rate drops away very badly, >> leading to many mispredictions. It only really makes sense to use higher >> decimation ratios, e.g. 64. >> >> --------- >> - [x] I confirm that I make this contribution in accordance with the >> [OpenJDK Interim AI Policy](https://openjdk.org/legal/ai). > > Andrew Haley has updated the pull request with a new target base due to a > merge or a rebase. The pull request now contains 215 commits: > > - Merge from master > - Better > - Crud > - Merge zarquon:/local/theRealAph-jdk into JDK-8134940 > - Cleanup > - Cleanup > - Revert "More" > > This reverts commit 3011e0fa6ef761ed7bb498a314b741b1d2f1bd90. > - Revert "Hacks to estimate profiling overhead." > > This reverts commit 4e2fdc2134a8057ad91e23642f66dc6a5728dce7. > - Hacks to estimate profiling overhead. > - More > - ... and 205 more: https://git.openjdk.org/jdk/compare/4be9a676...19eddaf2 These results look a lot more predictable. The reason for increased uncommon traps at lower capture ratios is that the overheads of profiling lead C1-compiled code to run more slowly, just because they're executing more instructions. At higher ratios it levels out, but increases again as C2 is fed with increasingly inaccurate data. I'm not certain as to the cause of the somewhat worse performance figures when the ratio is a more reasonable 64-128. It may be a simple matter of tuning: I note that with capture ratio = 1 (i.e. normal) a huge number of methods are compiled at Tier 2, and this barely happens with any capture ratio > 1. I have no idea what is causing this, but it looks significant. As this is an experimental patch, perhaps this could be investigated later. fedora:scratch $ sh ./run-renaissance.sh.1 dotty -r 20 dotty -r 20 --- Patched, PCR=1 Run 1: 4716 1465 1022 878 773 684 609 593 541 524 511 486 459 455 440 435 439 449 422 425 Run 2: 4750 1547 1056 860 745 666 619 572 560 517 506 495 472 472 464 423 425 426 424 437 Run 3: 4692 1496 1024 854 766 660 609 589 542 526 500 483 462 447 457 423 429 454 414 399 Tier1: 0.072 s, 9818 bytes, 2150 methods; Tier2: 2.139 s, 1013292 bytes, 5280 methods; Tier3: 8.380 s, 2514244 bytes, 13530 methods; Tier4: 45.351 s, 6130314 bytes, 6510 methods; Uncommon traps hit: 459 --- Patched, PCR=4 Run 1: 5600 1969 1281 985 833 756 717 685 642 631 611 608 583 573 572 561 542 559 540 540 Run 2: 5564 1914 1278 983 817 755 693 681 639 629 610 619 586 589 571 561 550 551 540 535 Run 3: 5594 1972 1291 1005 843 754 703 682 650 623 625 614 606 572 567 553 558 557 547 526 Tier1: 0.065 s, 9733 bytes, 2131 methods; Tier2: 0.181 s, 91461 bytes, 344 methods; Tier3: 8.684 s, 2717037 bytes, 16105 methods; Tier4: 36.642 s, 5319785 bytes, 8303 methods; Uncommon traps hit: 579 --- Patched, PCR=16 Run 1: 5139 1793 1250 936 798 733 660 630 608 592 582 585 563 552 546 543 527 533 517 506 Run 2: 5004 1769 1186 937 807 731 678 655 639 608 587 588 572 557 557 542 531 533 530 531 Run 3: 5071 1731 1206 939 812 734 662 627 631 586 574 598 560 565 554 554 564 534 530 510 Tier1: 0.063 s, 9751 bytes, 2137 methods; Tier2: 0.228 s, 129309 bytes, 545 methods; Tier3: 8.565 s, 2713460 bytes, 16066 methods; Tier4: 36.063 s, 5103268 bytes, 8085 methods; Uncommon traps hit: 535 --- Patched, PCR=64 Run 1: 4751 1700 1217 920 796 714 660 605 583 563 546 548 537 524 515 516 502 490 495 487 Run 2: 4806 1686 1130 886 769 696 644 634 583 570 562 556 535 541 526 538 524 524 510 512 Run 3: 4794 1638 1136 922 770 694 652 588 565 570 544 551 533 516 529 508 506 496 497 480 Tier1: 0.072 s, 9745 bytes, 2135 methods; Tier2: 0.375 s, 230407 bytes, 987 methods; Tier3: 8.585 s, 2695910 bytes, 15918 methods; Tier4: 35.684 s, 5050715 bytes, 7703 methods; Uncommon traps hit: 518 --- Patched, PCR=128 Run 1: 4719 1626 1120 894 762 678 631 599 561 531 524 525 508 522 496 488 475 467 469 468 Run 2: 4625 1593 1149 911 762 680 619 576 576 560 519 519 507 508 490 484 495 469 470 477 Run 3: 4786 1606 1114 867 737 651 604 591 552 531 526 527 523 497 494 486 485 497 474 458 Tier1: 0.070 s, 9756 bytes, 2138 methods; Tier2: 0.455 s, 288069 bytes, 1445 methods; Tier3: 8.476 s, 2695056 bytes, 15765 methods; Tier4: 35.774 s, 5112199 bytes, 7412 methods; Uncommon traps hit: 523 --- Patched, PCR=256 Run 1: 4709 1655 1149 881 751 676 621 577 538 549 513 505 494 490 497 487 470 470 464 475 Run 2: 4584 1511 1076 840 741 672 611 584 545 536 528 502 530 499 479 482 489 471 462 465 Run 3: 4517 1537 1114 865 766 669 612 587 553 527 530 501 507 486 484 485 468 482 490 481 Tier1: 0.066 s, 9789 bytes, 2143 methods; Tier2: 0.633 s, 320269 bytes, 1734 methods; Tier3: 8.562 s, 2684664 bytes, 15659 methods; Tier4: 36.094 s, 5165114 bytes, 7352 methods; Uncommon traps hit: 531 --- Patched, PCR=1024 Run 1: 4631 1637 1141 891 753 673 621 586 542 551 520 511 489 495 486 493 476 464 464 453 Run 2: 4646 1522 1098 875 735 682 610 559 534 523 501 493 486 480 469 473 461 453 460 473 Run 3: 4619 1544 1133 877 752 661 613 556 538 536 495 488 495 476 470 471 456 459 450 453 Tier1: 0.069 s, 9774 bytes, 2141 methods; Tier2: 0.820 s, 420536 bytes, 1912 methods; Tier3: 8.401 s, 2653668 bytes, 15504 methods; Tier4: 38.971 s, 5263978 bytes, 7524 methods; Uncommon traps hit: 539 --- Patched, PCR=4096 Run 1: 4486 1605 1129 915 774 668 638 592 565 531 518 523 504 494 498 480 497 465 464 457 Run 2: 4830 1547 1060 884 755 634 628 582 549 536 515 507 487 495 486 493 469 466 464 455 Run 3: 4703 1628 1170 943 818 706 639 596 565 542 516 529 496 483 475 493 469 461 475 453 Tier1: 0.066 s, 9758 bytes, 2140 methods; Tier2: 1.117 s, 575443 bytes, 2738 methods; Tier3: 8.045 s, 2554912 bytes, 14804 methods; Tier4: 37.619 s, 4888283 bytes, 6976 methods; Uncommon traps hit: 528 --- Patched, PCR=16384 Run 1: 4336 1474 1065 880 784 723 691 664 619 610 596 577 555 557 551 529 540 517 520 491 Run 2: 4475 1513 1131 913 812 742 695 636 601 623 577 579 546 553 540 531 533 513 518 505 Run 3: 4380 1485 1103 941 768 708 698 658 622 589 583 572 554 538 533 539 519 528 511 508 Tier1: 0.067 s, 9849 bytes, 2161 methods; Tier2: 1.359 s, 792786 bytes, 4410 methods; Tier3: 7.497 s, 2349698 bytes, 13160 methods; Tier4: 32.239 s, 3999967 bytes, 8150 methods; Uncommon traps hit: 1041 --- Patched, PCR=65536 Run 1: 4567 1812 1477 1125 1058 982 911 851 815 818 790 750 723 730 691 686 662 680 655 652 Run 2: 4594 1881 1399 1135 1050 961 931 866 843 807 792 752 740 737 714 711 684 670 677 661 Run 3: 4635 1886 1446 1190 1134 1003 970 888 848 813 782 771 741 727 720 715 691 685 664 653 Tier1: 0.084 s, 9791 bytes, 2148 methods; Tier2: 0.301 s, 153499 bytes, 1011 methods; Tier3: 8.450 s, 2543573 bytes, 14980 methods; Tier4: 30.574 s, 3681626 bytes, 7863 methods; Uncommon traps hit: 1225 Some interesting ideas there. The jump to the slow stub should be executed rarely, so the fast path is small. It is this: `cmp $0x40000000,%r14d; jb stub; xor %ebx,%ebx; crc32 %ebx,%r14d` However, in the cases where a counter is zero at code generation time (about 50% of static cases, presumably far less for dynamic execution) we have to insert a lot more code. In that case it might be worthwhile inserting the rest inline, but I suspect that's the rare case. As for the x86 microarch stuff, OK, but I doubt that's it. I've been mostly testing on AArch64, where I see similar behaviour. I've added a flag, `AggressiveProfileReduction`. With this flag, counters whose values are unknown at compiler time are treated differently. By default, we don't do profile decimation at all. When `AggressiveProfileReduction` is on, we emit a 0->1 test in these cases and then proceed to test and step the RNG. When a counter is zero at compile time, we just increment the counter, regardless of `ProfileCaptureRatio`. With these changes, single-threaded performance is neutral with `ProfileCaptureRatio` set to something reasonable such as 64 or 128. Multi-threaded performance is better, but not quite as good as with `AggressiveProfileReduction`. I think this is a reasonable compromise. I haven't yet touched Arm A32. ------------- PR Comment: https://git.openjdk.org/jdk/pull/28541#issuecomment-5835566894 PR Comment: https://git.openjdk.org/jdk/pull/28541#issuecomment-5875576488 PR Comment: https://git.openjdk.org/jdk/pull/28541#issuecomment-5955374387
