TLDR; I miss the glory days of my 2500k. Things were much simpler…lol The amount of pre-overclocking efforts required to solve this nut is insane. Before I could just say “lulz this guy got 5.0GHz with this motherboard and 1.45v let me start there.”
Recap:
This all started because I wanted to play Starfield, and even with my 4070 Ti (which I bought primarily for non gaming things) my performance was dog shit. Then I watched the whole GN/Intel thing about “GPU Busy” thing.
Moving on
Got my ROG board in today. Spent a bunch of time considering my approach here. Some resources I found that were relevant:
Really good overclocking guide for the regular 7900X:
SkatterBencher #46: AMD Ryzen 9 7900X Overclocked to 5900 MHz - SkatterBencher
How to use Hydra:
HYDRA BIG TUTORIAL - Google Slides
BuildZoid Talking eclk:
This post is largely influenced by their testing methodology but I think my spin and path forward on it remain “different enough” to document my attempt to solve the problem.
Here’s my current list of foundational assumptions:
- We know AMD locked the multiplier and vcore adjustments on X3D.
- We know Curve Optimizer adjusts voltage on a per-core level.
- We know LLC compensates for vdroop by giving you a higher starting voltage.
- We know we can use eclk to simulate the affects of a multiplier, and eek out better base clocks.
- We know in 2023 overclocking is not that simple. Precision Boost is inherent to the chip design.
- PBO can be leveraged as a tool to eek out better boost clocks.
- Ryzen 7000 can get hot when you start drawing current, thermal density problems
Here’s my current plan:
Hydra:
WARNING: From my experience, adjustments made in Hydra, by Hydra are >ADDITIVE in nature. What that means is if you set a curve optimizer value in your BIOS of -30, >and you also have a cure optimizer value set to -30…when Hydra opens you will have a value of >-60. It’s certainly possible to extend beyond the limits in place by AMD and you can potentially >damage your CPU.
It’s probably a good idea to
- DELETE your copy of Hydra between runs and re-extract it each time you want to do a scan.
- NEVER make changes to the BIOS before a scan is completed.
- NEVER open a non-freshly extracted Hydra after making an overclocking related BIOS change
I can leverage Hydra to find my strongest and weakest cores. Hydra basically optimizes your CPU on a core-by-core basis to draw the least amount of current at a target boost speed. So I don’t have to rely on just making a blanket assumption that ccd0 will be more efficient than ccd1. I will have the data to prove it and adjust accordingly.
In all honestly, I still don’t know what Hydra does beyond that (from a CPU perspective, anyway) but the X3D doesn’t support Hybrid OC anyway AFAIK so I am only using it as a discovery tool.
I wrote a little script to parse Hydra’s logs (they are way too long to parse manually IMO) to yank out the information that matters to me and spit out a csv for me.
import re
import csv
import os
import datetime
import glob
# This code block performs the following tasks:
# 1. It compiles regular expressions patterns using the re.compile() function.
# - The first pattern, core_pattern, matches the string "CORE#" followed by one or more digits (\d+),
# followed by one or more whitespace characters (\s+), followed by the string "CPPC: " and one or more digits (\d+),
# followed by one or more whitespace characters (\s+), followed by the string "AMD CO: " and a signed integer (-?\d+).
# - The second pattern, test_pattern, matches the string "EFREQ: " followed by one or more digits (\d+).
# 2. It initializes two empty dictionaries, results and co_values.
# 3. It opens the file specified by the filename variable in read mode and reads all the lines into a list called lines.
# 4. It iterates over each line in the lines list.
# - It searches for a match of the core_pattern and test_pattern in each line using the search() method of the compiled patterns.
# - If a match is found for the core_pattern, it extracts the core number and CO value from the match using the group() method.
# It then adds the CO value to the list associated with the current_core key in the co_values dictionary.
# 5. It filters the results dictionary based on the CO values.
# - It creates a new dictionary called filtered_results, which contains only the key-value pairs from results
# where the CO value is in the top 3 CO values for the corresponding core.
# 6. It sorts the efreqs (EFREQ values) in descending order for each key in filtered_results and keeps only the top 3 values.
# - It creates a new dictionary called top_results, which contains the key-value pairs from filtered_results
# with the top 3 EFREQ values for each key.
# 7. It determines the directory path of the input filename and creates an output filename by joining the directory path
# with the string 'results.csv'.
# 8. It opens the output_filename in write mode and creates a csv.writer object called csv_writer.
# 9. It writes the header row to the CSV file with the column names: "Core", "CO Value", "Highest EFREQ",
# "2nd Highest EFREQ", and "3rd Highest EFREQ".
# 10. It iterates over the key-value pairs in top_results and writes each row to the CSV file.
# - Each row consists of the core number, CO value, and the top
def parse_lines(lines):
print("Parsing lines...")
core_pattern = re.compile(r"CORE#(\d+)\s+CPPC: \d+\s+AMD CO: (-?\d+)")
test_pattern = re.compile(r"EFREQ: (\d+)")
results = {}
co_values = {}
current_core = None
current_co = None
for line in lines:
core_match = core_pattern.search(line)
test_match = test_pattern.search(line)
if core_match:
current_core = f"CORE#{core_match.group(1)}"
current_co = int(core_match.group(2))
co_values.setdefault(current_core, []).append(current_co)
if test_match and current_core and current_co is not None:
efreq_value = int(test_match.group(1))
results.setdefault((current_core, current_co), []).append(efreq_value)
return results, co_values
def filter_and_sort_results(results, co_values):
print("Filtering and sorting results...")
filtered_results = {(core, co): efreqs for (core, co), efreqs in results.items() if co in sorted(co_values.get(core, []))[:3]}
top_results = {key: sorted(efreqs, reverse=True)[:3] for key, efreqs in filtered_results.items()}
return top_results
def export_to_csv(top_results, source_filename):
print(f"Exporting results for {source_filename} to CSV...")
directory = os.path.dirname(source_filename)
base_name = os.path.splitext(os.path.basename(source_filename))[0] # Extracting the file name without extension
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
output_filename = os.path.join(directory, f'{base_name}-filtered-results-{timestamp}.csv')
with open(output_filename, 'w', newline='') as csvfile:
csv_writer = csv.writer(csvfile)
csv_writer.writerow(["Core", "CO Value", "Highest EFREQ", "2nd Highest EFREQ", "3rd Highest EFREQ"])
for key, values in top_results.items():
row = [key[0], key[1]] + values + [None] * (3 - len(values))
csv_writer.writerow(row)
print(f"Results exported to {output_filename}")
def extract_highest_efreq(filename):
print(f"\nExtracting highest EFREQ for file: {filename}")
with open(filename, 'r') as f:
lines = f.readlines()
results, co_values = parse_lines(lines)
top_results = filter_and_sort_results(results, co_values)
export_to_csv(top_results, filename)
def process_all_files(directory):
print(f"Starting processing for directory: {directory}")
# This will match all .txt files in the given directory
for filename in glob.glob(os.path.join(directory, '*.txt')):
print(f"Processing file: {filename}")
extract_highest_efreq(filename)
print("\n")
# Target directory (can be changed as needed)
directory = "c:\\oc\\HYDRA_1.5C_PRO\\HYDRA 1.5C PRO\\LOGS"
process_all_files(directory)
Which should format it nicely with:
CORE, CO VALUE, and MAXIMUM CLOCK
I asked it to give me 3 values each for CO VALUE and CLOCK. These numbers are going to my baseline numbers to work from.
Math Time?
I want to increase baseclock and allow boost clocks to rise proportionally, but only if possible. This will maintain the performance characteristics software is being designed to expect.
Next set of assumptions:
- We know voltage=heat
- We know overclocking needs voltage
- We should leverage CO, PBO and eclk to develop our solution.
- I am developing “ASIC Quality” Logic for each core, based on its CO value from Hydra.
- A lower CO value equals a higher ASIC quality
We know from GPU-Z ASIC quality that we can generally assume some GPUs want more voltage than others at similar clocks. This actually makes them less efficient but potentially more overclockable.
This next bit isn’t perfect given my assumptions
Core Highest EFREQ
CORE#0 5157
CORE#1 5156
CORE#2 5149
CORE#3 5156
CORE#4 5156
CORE#5 5155
CORE#6 5479
CORE#7 5520
CORE#8 5454
CORE#9 5469
CORE#10 5453
CORE#11 5419
And I’m going to notate the highest frequency listed here and use that as the basis for defining my new fmax…with an eclk twist. In this case it’s 5520
Using SkatterBencher’s formula:
With ECLK, we still build on the factory-fused VFT curve but adjust the frequency by adjusting the reference clock. For example, if the Precision Boost has a VFT point for 5000 MHz at 1.15V at 50C, with an ECLK of 105 MHz, the actual point will be 5250 MHz at 1.15V at 50C.
Baseclock is a bit easier to calculate but because I am dumb and lazy,I’ll let chatgpt do the maths for this
WARNING: don’t use my numbers, use your own math! They are EXAMPLES only. Not real data.
ChatGPT says (truncated for brevity in the forums)
For ECLK = 106 MHz:
**Boost Clock:**
Adjusted Frequency = 5520 * (106/100) = 5851.2 MHz
**Base Clock:**
Frequency = 44 * 106 = 4664 MHz
For ECLK = 108 MHz:
**Boost Clock:**
Adjusted Frequency = 5520 * (108/100) = 5965.6 MHz
**Base Clock:**
Frequency = 44 * 108 = 4752 MHz
For ECLK = 110 MHz:
**Boost Clock:**
Adjusted Frequency = 5520 * (110/100) = 6072 MHz
**Base Clock:**
Frequency = 44 * 110 = 4840 MHz
I’m also going to attack the CO front and do some LLC calibration to more broadly to find stability in the above clock targets. Let’s start by using the corresponding CO values for the frequency examples above, as extracted from Hydra’s scanning.
I’m going to keep iterating each of these values by +1 until the first core reaches +30, which IIRC is as high as you can go with a positive CO in BIOS on my new board. In this case CORE 6 and 7 are -12, to get them to +30 I need to add 42.
Core |
Hydra CO Value |
Adjusted CO Value |
CORE#0 |
-48 |
-6 |
CORE#1 |
-39 |
+3 |
CORE#2 |
-33 |
+9 |
CORE#3 |
-31 |
+11 |
CORE#4 |
-39 |
+3 |
CORE#5 |
-36 |
+6 |
CORE#6 |
-12 |
+30 |
CORE#7 |
-12 |
+30 |
CORE#8 |
-18 |
+24 |
CORE#9 |
-18 |
+24 |
CORE#10 |
-18 |
+24 |
CORE#11 |
-18 |
+24 |
GOALS:
Given previous testing at stock, I should expect somewhere in this neighborhood running 4.6 for ccd0 and 4.8 GHz for ccd1 in Cinebench.
Given clocks at stock while playing Starfield are ~5.0-5.1 GHz pretty consistently in that workload.
I am going to work through the above permutations and document the next part of my journey. I’m still not quite sure where I’ll land just yet. I’d be happy to see 5.2-5.4 in Starfield and 4.8-5.2 in Cinebench.
Really looking to get somewhere between eclk 106-112 stable, just no idea what to expect.
Methodology:
I’m going to set the real CO numbers I get from finishing a pass of Hydra. The only thing I cared to set was negative fclock target (5650 is stock) of 5450. When It’s done:
- I’m going to set PBO to a corresponding -200 max boost for now. This means the adjusted target maximum boost frequency above will be subtracted by 200.
- I’m going to set the adjusted CO values.
- I’m going to set LLC to 5.
Then I am going to start working my way up the eclk train 2 at a time until Windows won’t boot. Then I am going to back it off by 2 and increase LLC to 6. If that is still not stable I will back off eclck by 1. Other knobs and tubes, Rinse and repeat until I find real-world stability over time.
Delidding mods will only come after, and I will redo alot of this math and testing given the new thermal environment at that point.
Personal Sidebar:
I am probably now overcompensating for my systems engineering on my workstation, though, its primary purpose is only sometimes gaming.
Gaming was always a social thing for me. These days, there’s not alot of “social” gaming with my friends. We all kind of fell off the League train…and not enough of them go into Diablo, as an example, to pick up something new with them. More or less, my friend group and I are busy adulting more and more often anyway so hasn’t been a priority for me to invest in gaming. But I do still get obsessed with a good RPG for a few weeks and then move on to other media/hobbies. I’d like to reinvest in that part of my system.