diff --git a/Program.cs b/Program.cs index 2d4e48a..4f5bd83 100644 --- a/Program.cs +++ b/Program.cs @@ -1,14 +1,12 @@ using System; using System.Buffers.Binary; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Net.Sockets; using System.Numerics; using System.Security.Cryptography; -using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Threading; @@ -87,11 +85,10 @@ internal static class Program password = GetArg(args, "-p") ?? "x"; var threadText = GetArg(args, "-t"); - var threadsAuto = threadText == null; - threadCount = threadsAuto - ? GetPhysicalCoreCount() - : int.Parse(threadText!, CultureInfo.InvariantCulture); + threadCount = threadText != null + ? int.Parse(threadText, CultureInfo.InvariantCulture) + : Environment.ProcessorCount; if(threadCount < 1) throw new ArgumentException("Thread count must be at least 1"); @@ -128,12 +125,9 @@ internal static class Program }; PrintBanner(); - PrintStartupDiagnostics(); - Console.WriteLine($"Pool : {host}:{port}"); - Console.WriteLine($"Worker : {worker}"); - Console.WriteLine( - $"Mining Threads : {threadCount}" + - (threadsAuto ? " (auto)" : " (manual)")); + Console.WriteLine($"Pool : {host}:{port}"); + Console.WriteLine($"Worker : {worker}"); + Console.WriteLine($"Threads : {threadCount}"); Console.WriteLine(); var statsTask = StatsLoopAsync(shutdown.Token); @@ -820,235 +814,6 @@ internal static class Program } } - private static void PrintStartupDiagnostics() - { - var logicalCpus = Environment.ProcessorCount; - var physicalCores = GetPhysicalCoreCount(); - var cpuModel = GetCpuModel(); - var aesSupported = GetAesSupport(); - var hugePages = GetHugePagesStatus(); - - string randomXStatus; - string randomXPath; - - if(OperatingSystem.IsLinux()) - { - // Accessing these properties initializes VextaRandomX and - // explicitly loads librandomx.so from the miner directory. - randomXStatus = VextaRandomX.NativeLibraryLoaded - ? "Loaded OK" - : "Not loaded"; - - randomXPath = VextaRandomX.NativeLibraryPath ?? "Unknown"; - } - else - { - randomXStatus = "System loader"; - randomXPath = "Platform default"; - } - - Console.WriteLine($"CPU : {cpuModel}"); - Console.WriteLine($"Physical Cores : {physicalCores}"); - Console.WriteLine($"Logical CPUs : {logicalCpus}"); - Console.WriteLine($"AES : {aesSupported}"); - Console.WriteLine($"Huge Pages : {hugePages}"); - Console.WriteLine($"RandomX Library: {randomXStatus}"); - Console.WriteLine($"Library Path : {randomXPath}"); - Console.WriteLine(); - } - - private static string GetCpuModel() - { - try - { - if(OperatingSystem.IsLinux() && File.Exists("/proc/cpuinfo")) - { - foreach(var line in File.ReadLines("/proc/cpuinfo")) - { - if(line.StartsWith("model name", StringComparison.OrdinalIgnoreCase)) - { - var colon = line.IndexOf(':'); - - if(colon >= 0) - return line[(colon + 1)..].Trim(); - } - } - } - - var identifier = Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER"); - - if(!string.IsNullOrWhiteSpace(identifier)) - return identifier.Trim(); - } - catch - { - } - - return "Unknown"; - } - - private static int GetPhysicalCoreCount() - { - try - { - if(OperatingSystem.IsLinux() && File.Exists("/proc/cpuinfo")) - { - var cores = new HashSet(); - string? physicalId = null; - string? coreId = null; - - foreach(var rawLine in File.ReadLines("/proc/cpuinfo")) - { - var line = rawLine.Trim(); - - if(line.Length == 0) - { - if(physicalId != null && coreId != null) - cores.Add($"{physicalId}:{coreId}"); - - physicalId = null; - coreId = null; - continue; - } - - if(line.StartsWith("physical id", StringComparison.OrdinalIgnoreCase)) - { - var colon = line.IndexOf(':'); - - if(colon >= 0) - physicalId = line[(colon + 1)..].Trim(); - } - else if(line.StartsWith("core id", StringComparison.OrdinalIgnoreCase)) - { - var colon = line.IndexOf(':'); - - if(colon >= 0) - coreId = line[(colon + 1)..].Trim(); - } - } - - if(physicalId != null && coreId != null) - cores.Add($"{physicalId}:{coreId}"); - - if(cores.Count > 0) - return cores.Count; - } - - if(OperatingSystem.IsWindows()) - { - var windowsCores = GetWindowsPhysicalCoreCount(); - - if(windowsCores > 0) - return windowsCores; - } - } - catch - { - } - - return Environment.ProcessorCount; - } - - private static int GetWindowsPhysicalCoreCount() - { - uint length = 0; - - GetLogicalProcessorInformation(IntPtr.Zero, ref length); - - if(length == 0) - return 0; - - var buffer = Marshal.AllocHGlobal((int)length); - - try - { - if(!GetLogicalProcessorInformation(buffer, ref length)) - return 0; - - // SYSTEM_LOGICAL_PROCESSOR_INFORMATION is 32 bytes on x64 - // and 24 bytes on x86. - var entrySize = IntPtr.Size == 8 ? 32 : 24; - var relationshipOffset = IntPtr.Size; - var count = 0; - - for(var offset = 0; - offset + entrySize <= length; - offset += entrySize) - { - var relationship = Marshal.ReadInt32( - buffer, - offset + relationshipOffset); - - // RelationProcessorCore = 0 - if(relationship == 0) - count++; - } - - return count; - } - finally - { - Marshal.FreeHGlobal(buffer); - } - } - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool GetLogicalProcessorInformation( - IntPtr buffer, - ref uint returnedLength); - - private static string GetAesSupport() - { - try - { - if(System.Runtime.Intrinsics.X86.Aes.IsSupported) - return "Supported"; - } - catch - { - } - - return "Not detected"; - } - - private static string GetHugePagesStatus() - { - try - { - if(OperatingSystem.IsLinux() && File.Exists("/proc/meminfo")) - { - foreach(var line in File.ReadLines("/proc/meminfo")) - { - if(!line.StartsWith("HugePages_Total:", StringComparison.Ordinal)) - continue; - - var value = line["HugePages_Total:".Length..].Trim(); - var parts = value.Split( - ' ', - StringSplitOptions.RemoveEmptyEntries); - - if(parts.Length > 0 && - long.TryParse( - parts[0], - NumberStyles.Integer, - CultureInfo.InvariantCulture, - out var total)) - { - return total > 0 - ? $"Configured ({total})" - : "Not configured"; - } - } - } - } - catch - { - } - - return "Unknown"; - } - private static async Task StatsLoopAsync(CancellationToken ct) { var timer = new PeriodicTimer(TimeSpan.FromSeconds(10)); @@ -1058,10 +823,6 @@ internal static class Program var lastTime = sw.Elapsed; var measuring = false; - var averageHistory = new List<(TimeSpan Time, long Hashes)>(); - TimeSpan? averageStartTime = null; - var nextAverageReport = TimeSpan.Zero; - try { while(await timer.WaitForNextTickAsync(ct)) @@ -1069,34 +830,6 @@ internal static class Program var now = sw.Elapsed; var hashes = Interlocked.Read(ref totalHashes); - if(hashes > 0) - { - if(averageStartTime == null) - { - averageStartTime = now; - nextAverageReport = now + TimeSpan.FromMinutes(1); - averageHistory.Add((now, hashes)); - } - else if(now >= nextAverageReport) - { - averageHistory.Add((now, hashes)); - - WriteAverageHashrate( - now, - hashes, - averageHistory, - averageStartTime.Value); - - do - { - nextAverageReport += TimeSpan.FromMinutes(1); - } - while(nextAverageReport <= now); - - TrimAverageHistory(now, averageHistory); - } - } - if(hashes == 0) { lastHashes = hashes; @@ -1144,74 +877,6 @@ internal static class Program } } - private static void WriteAverageHashrate( - TimeSpan now, - long hashes, - List<(TimeSpan Time, long Hashes)> history, - TimeSpan startTime) - { - var avg5 = FormatAverageWindow( - now, hashes, history, startTime, TimeSpan.FromMinutes(5)); - - var avg15 = FormatAverageWindow( - now, hashes, history, startTime, TimeSpan.FromMinutes(15)); - - var avg60 = FormatAverageWindow( - now, hashes, history, startTime, TimeSpan.FromMinutes(60)); - - WriteColor( - $"AVG HASHRATE 5m:{avg5} 15m:{avg15} 60m:{avg60}", - ConsoleColor.DarkCyan); - } - - private static string FormatAverageWindow( - TimeSpan now, - long hashes, - List<(TimeSpan Time, long Hashes)> history, - TimeSpan startTime, - TimeSpan window) - { - if(now - startTime < window) - return "warming up"; - - var cutoff = now - window; - (TimeSpan Time, long Hashes)? sample = null; - - foreach(var item in history) - { - if(item.Time <= cutoff) - sample = item; - else - break; - } - - if(sample == null) - return "warming up"; - - var elapsed = (now - sample.Value.Time).TotalSeconds; - - if(elapsed <= 0) - return "warming up"; - - var deltaHashes = hashes - sample.Value.Hashes; - var rate = deltaHashes / elapsed; - - return FormatHashrate(rate); - } - - private static void TrimAverageHistory( - TimeSpan now, - List<(TimeSpan Time, long Hashes)> history) - { - var keepAfter = now - TimeSpan.FromMinutes(62); - - while(history.Count > 2 && - history[1].Time < keepAfter) - { - history.RemoveAt(0); - } - } - private static string FormatHashrate(double value) { if(value >= 1_000_000) @@ -1291,7 +956,7 @@ internal static class Program Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(); - Console.WriteLine("Vexta CPU Miner 0.1.2"); + Console.WriteLine("Vexta CPU Miner 0.1.1"); Console.ForegroundColor = ConsoleColor.DarkGray; Console.WriteLine("Copyright (c) 2026 Vexta Project"); @@ -1331,7 +996,7 @@ internal static class Program Console.WriteLine(" -u Vexta address / worker name"); Console.WriteLine(" -p Password (default: x)"); Console.WriteLine( - " -t CPU threads (default: auto-detect physical cores)"); + " -t CPU threads (default: logical CPU count)"); Console.WriteLine(); Console.WriteLine("Ctrl+C stops the miner cleanly."); } diff --git a/README.md b/README.md index e62caa5..0940212 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,8 @@ Prebuilt binaries are available for: - Vexta RandomX mining - RandomX full-memory mode - Windows x64 and Linux x64 support -- Automatic CPU thread selection based on physical cores -- Manual CPU thread override with `-t` -- Startup diagnostics for CPU, cores, AES, Huge Pages, and RandomX library +- Configurable CPU thread count - Local hashrate display -- 5 / 15 / 60 minute average hashrate - Accepted / rejected share statistics - Automatic RandomX seed changes - Daemon-confirmed `BLOCK FOUND` notifications when supported by the pool @@ -62,7 +59,7 @@ Example: | `-o` | Stratum pool address | | `-u` | Vexta wallet address, optionally followed by `.worker` | | `-p` | Pool password, normally `x` | -| `-t` | Number of CPU mining threads (optional; auto-detects physical cores when omitted) | +| `-t` | Number of CPU mining threads | ## Requirements @@ -72,11 +69,6 @@ Example: RandomX full-memory mode is enabled. -On startup, the miner reports detected CPU information, physical and logical CPU counts, -AES support, Huge Pages status, RandomX library status, and the selected mining thread count. - -If `-t` is omitted, the miner automatically uses the detected physical core count. - ## Building from Source ### Requirements diff --git a/VextaRandomX.cs b/VextaRandomX.cs index d00dff0..7400d88 100644 --- a/VextaRandomX.cs +++ b/VextaRandomX.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Threading; using System.Collections.Concurrent; using System.Collections.Generic; @@ -10,51 +9,6 @@ namespace Vexta.CpuMiner; public static unsafe class VextaRandomX { - private static IntPtr nativeLibraryHandle = IntPtr.Zero; - - public static string? NativeLibraryPath { get; private set; } - - public static bool NativeLibraryLoaded => nativeLibraryHandle != IntPtr.Zero; - - static VextaRandomX() - { - if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - var libraryPath = Path.Combine(AppContext.BaseDirectory, "librandomx.so"); - - if(!File.Exists(libraryPath)) - throw new DllNotFoundException( - $"RandomX library was not found at '{libraryPath}'."); - - try - { - nativeLibraryHandle = NativeLibrary.Load(libraryPath); - NativeLibraryPath = libraryPath; - } - catch(Exception ex) - { - throw new DllNotFoundException( - $"Unable to load RandomX library from '{libraryPath}': {ex.Message}", - ex); - } - - NativeLibrary.SetDllImportResolver( - typeof(VextaRandomX).Assembly, - (libraryName, assembly, searchPath) => - { - if(string.Equals( - libraryName, - "librandomx", - StringComparison.Ordinal)) - { - return nativeLibraryHandle; - } - - return IntPtr.Zero; - }); - } - } - [Flags] public enum RandomXFlags {