I see there are several node packages that allow you to look up a specific process's usage, such as https://www.npmjs.com/package/usage
I am trying to get the overall sever usage/stats (CPU and Memory), not just one specific process or another. Maybe even disk space usage.
I am currently unable to find anything like this, is this possible?
The native module os can give you some memory and cpu usage statistics.
var os = require('os');
console.log(os.cpus());
console.log(os.totalmem());
console.log(os.freemem())
The cpus() function gives you an average, but you can calculate the current usage by using a formula and an interval, as mentioned in this answer.
There is also a package that does this for you, called os-utils.
Taken from the example on github:
var os = require('os-utils');
os.cpuUsage(function(v){
console.log( 'CPU Usage (%): ' + v );
});
For information about the disk you can use diskspace
Check node-os-utils
CPU average usage
Free and used drive space
Free and used memory space
Operating System
All processes running
TTY/SSH opened
Total opened files
Network speed (input and output)
var osu = require('node-os-utils')
var cpu = osu.cpu
cpu.usage()
.then(info => {
console.log(info)
})
As I found no code to solve this problem and don't want to rely on other packages just for some lines of code, I wrote a function that calculates the average CPU load between two successive function calls. I am assuming t_idle + t_user + t_sys = total cpu time and results are kind of similar to the ones of my Windows task manager, however, the usage seems little more sensitive to me (e.g. music playback increases the cpu load more than in the Windows task manager). Please corret me if my assumptions are wrong.
const os = require('os');
// Initial value; wait at little amount of time before making a measurement.
let timesBefore = os.cpus().map(c => c.times);
// Call this function periodically e.g. using setInterval,
function getAverageUsage() {
let timesAfter = os.cpus().map(c => c.times);
let timeDeltas = timesAfter.map((t, i) => ({
user: t.user - timesBefore[i].user,
sys: t.sys - timesBefore[i].sys,
idle: t.idle - timesBefore[i].idle
}));
timesBefore = timesAfter;
return timeDeltas
.map(times => 1 - times.idle / (times.user + times.sys + times.idle))
.reduce((l1, l2) => l1 + l2) / timeDeltas.length;
}
You can also use process.cpuUsage() to return the system and user cpu time in microseconds. It can also calculate the difference to a previous call.
https://nodejs.org/api/process.html#process_process_cpuusage_previousvalue
Node.js has os.loadavg() method
// Require os module
const os = require('os');
// Printing os.loadavg() value
var avg_load = os.loadavg();
console.log("Load average (1 minute):"
+ String(avg_load[0]));
console.log("Load average (5 minute):"
+ String(avg_load[1]));
console.log("Load average (15 minute):"
+ String(avg_load[2]));
More info here
async function getinfo() {
const cpu = await si.cpu();
const disk = (await si.diskLayout())[0];
const os = await si.osInfo();
const versions = await si.versions();
const ram = await si.mem();
// CPU Info
let info = `CPU: ${cpu.manufacturer} ${cpu.brand} ${cpu.speed}GHz\n`;
info += `Cores: ${cpu.cores} (${cpu.physicalCores} Physical)\n`;
// RAM Info
const totalRam = Math.round(ram.total / 1024 / 1024 / 1024);
info += `RAM: ${totalRam}GB\n`;
// Disk Info
const size = Math.round(disk.size / 1024 / 1024 / 1024);
info += `Disk: ${disk.vendor} ${disk.name} ${size}GB ${disk.type} (${disk.interfaceType})\n`;
//OS Info
info += `OS: ${os.distro} ${os.codename} (${os.platform})\n`;
info += `Kernel: ${os.kernel} ${os.arch}\n`;
// Node Info
info += `Node: v${versions.node}\n`;
info += `V8: ${versions.v8}`;
return info;
}
Of course it is possible. But you'll need a C++ native module to do that. And keep in mind that every OS has their own way of querying system resource usage.
For example, if you're on Windows (which might be what you're looking for since usage doesn't support Windows), you could do something like
performance.cpp
#include <node.h>
#include "performance_algorithm.hpp"
using namespace v8;
void InitAll(Handle<Object> exports) {
PerformanceAlgorithm::Initialize();
PerformanceAlgorithm::RegisterMethod(exports);
}
NODE_MODULE(Performance, InitAll)
performance_algorithm.cpp
#include <algorithm>
#include "baton.hpp"
#include "structs.hpp"
#include "performance_algorithm.hpp"
void PerformanceAlgorithm::Initialize() {
PdhOpenQuery(NULL, NULL, &cpuQuery);
PdhAddCounter(cpuQuery, "\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal);
PdhCollectQueryData(cpuQuery);
}
void PerformanceAlgorithm::RegisterMethod(Handle<Object> exports) {
NODE_SET_METHOD(exports, "getPerformanceData", PerformanceAlgorithm::GetPerformanceDataAsync);
}
void PerformanceAlgorithm::GetPerformanceDataAsync(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
if (args.Length() != 1) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong number of arguments")));
} else {
if (!args[0]->IsFunction()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments type")));
} else {
Local<Function> callbackFunction = Local<Function>::Cast(args[0]);
Baton<string, PerformanceData>* baton = new Baton<string, PerformanceData>();
baton->request.data = baton;
baton->callbackFunction.Reset(isolate, callbackFunction);
uv_queue_work(uv_default_loop(), &baton->request, PerformanceAlgorithm::GetPerformanceDataWork, PerformanceAlgorithm::GetPerformanceDataAsyncAfter);
}
}
}
void PerformanceAlgorithm::GetPerformanceDataWork(uv_work_t* request) {
Baton<string, PerformanceData>* baton = static_cast<Baton<string, PerformanceData>*>(request->data);
baton->result.memory_info.dwLength = sizeof(MEMORYSTATUSEX);
GlobalMemoryStatusEx(&baton->result.memory_info);
PDH_FMT_COUNTERVALUE counterVal;
PdhCollectQueryData(cpuQuery);
PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal);
baton->result.cpu_usage = counterVal.doubleValue;
DWORD processIDs[1024], bytesReturned;
EnumProcesses(processIDs, sizeof(processIDs), &bytesReturned);
DWORD numberOfProcesses = bytesReturned / sizeof(DWORD);
for (int i = 0; i < numberOfProcesses; i++) {
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIDs[i]);
HMODULE hMods[1024];
DWORD cbNeeded;
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
for (int j = 0; j < (cbNeeded / sizeof(HMODULE)); j++) {
TCHAR szModName[MAX_PATH];
GetModuleFileNameEx(hProcess, hMods[j], szModName, sizeof(szModName) / sizeof(TCHAR));
ProcessInfo info;
info.process_id = processIDs[i];
info.path = string(szModName);
baton->result.processes.push_back(info);
break;
}
}
CloseHandle(hProcess);
}
sort(baton->result.processes.begin(), baton->result.processes.end(), [](ProcessInfo a, ProcessInfo b) -> bool {
return a.process_id < b.process_id;
});
GetPerformanceInfo(&baton->result.performance_info, sizeof(PERFORMACE_INFORMATION));
}
void PerformanceAlgorithm::GetPerformanceDataAsyncAfter(uv_work_t* request, int status) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
EscapableHandleScope escapableHandleScope(isolate);
Baton<string, PerformanceData>* baton = static_cast<Baton<string, PerformanceData>*>(request->data);
Local<Function> callbackFunction = Local<Function>::New(isolate, baton->callbackFunction);
Local<Object> returnValue = Object::New(isolate);
returnValue->Set(String::NewFromUtf8(isolate, "cpu_usage"), Number::New(isolate, baton->result.cpu_usage));
returnValue->Set(String::NewFromUtf8(isolate, "ram_usage"), Number::New(isolate, baton->result.memory_info.dwMemoryLoad));
returnValue->Set(String::NewFromUtf8(isolate, "total_physical_memory"), Number::New(isolate, baton->result.memory_info.ullTotalPhys));
returnValue->Set(String::NewFromUtf8(isolate, "available_physical_memory"), Number::New(isolate, baton->result.memory_info.ullAvailPhys));
returnValue->Set(String::NewFromUtf8(isolate, "total_page_file"), Number::New(isolate, baton->result.memory_info.ullTotalPageFile));
returnValue->Set(String::NewFromUtf8(isolate, "available_page_file"), Number::New(isolate, baton->result.memory_info.ullAvailPageFile));
returnValue->Set(String::NewFromUtf8(isolate, "total_virtual"), Number::New(isolate, baton->result.memory_info.ullTotalVirtual));
returnValue->Set(String::NewFromUtf8(isolate, "available_virtual"), Number::New(isolate, baton->result.memory_info.ullAvailVirtual));
Local<Array> processes = Array::New(isolate, baton->result.processes.size());
for (int i = 0; i < baton->result.processes.size(); i++) {
Local<Object> processInfo = Object::New(isolate);
processInfo->Set(String::NewFromUtf8(isolate, "process_id"), Number::New(isolate, baton->result.processes[i].process_id));
processInfo->Set(String::NewFromUtf8(isolate, "path"), String::NewFromUtf8(isolate, baton->result.processes[i].path.c_str()));
processes->Set(i, processInfo);
}
returnValue->Set(String::NewFromUtf8(isolate, "running_processes"), processes);
const unsigned int argc = 1;
Handle<Value> argv[argc] = { escapableHandleScope.Escape(returnValue) };
callbackFunction->Call(isolate->GetCurrentContext()->Global(), argc, argv);
baton->callbackFunction.Reset();
delete baton;
}
Related
I want to use a C-Function from Node.js by using N-API with node-addon-api module wrapper. This is the first time with N-API for me and I'm also a beginner with Node and C++. I have a experience in C programming of embedded systems but this Node.jS / N-API thing I don't understand completely yet...
What I wan't to do is to call a C-Function with this prototype from Node.js:
unsigned char *MyFunction(unsigned char *data, size_t size, size_t min, size_t max)
*data is a pointer to an array containing RGB image data [R0, G0, B0, R1, G1, B1, ...] with size size which should be processed in MyFunction (extracting RGB channels, inverting, ...).
What I have so far is this c++ code:
#include <napi.h>
using namespace Napi;
Napi::Value Method(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() != 3) {
Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
return env.Null();
}
else {
const Napi::Array inputArray = info[0].As<Napi::Array>();
const float smin = info[1].As<Napi::Number>().FloatValue();
const float smax = info[2].As<Napi::Number>().FloatValue();
const unsigned int length = inputArray.Length();
unsigned int i;
Napi::Array outputArray = Napi::Array::New(env, length);
Napi::Array redArray = Napi::Array::New(env, length / 3);
Napi::Array greenArray = Napi::Array::New(env, length / 3);
Napi::Array blueArray = Napi::Array::New(env, length / 3);
// Extract Channels
for (i = 0; i < (length / 3); i++) {
redArray[i] = inputArray[i * 3];
greenArray[i] = inputArray[i * 3 + 1];
blueArray[i] = inputArray[i * 3 + 2];
}
// Apply Simple Color Balance to each channel
for (i = 0; i < (length / 3); i++) {
outputArray[i] = redArray[i];
}
return redArray; // Test => this works
}
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "Test"),
Napi::Function::New(env, Method));
return exports;
}
NODE_API_MODULE(module, Init)
and this is the node part:
const sharp = require('sharp');
sharp('testPic.jpg')
.resize(800, 600)
.toFile('SharpOut.jpg')
.then( data => {
console.log('Resize & Negate => OK')
// Extract Channel data
sharp('SharpOut.jpg')
.raw()
.toBuffer()
.then(data => {
var smin = 0.0;
var smax = 0.0;
var testArr = [];
for (let n = 0; n < data.length; n++) {
testArr[n] = data[n];
}
const HelloWorld = require('./build/Release/module.node');
const result = HelloWorld.Test(testArr, smin, smax);
})
.catch(err => {
console.log('ERROR during extraction of RED channel. ERR:');
console.log(err);
});
})
.catch( err => {
console.log('ERROR');
console.log(err);
});
My problems
Sharp outputs a buffer and not an array but with ArrayBuffer instead of Array I was not able to get working code. Compiling is ok but when I execute it in node I'm getting
Error: Invalid argument at D:\temp\testSCB\index.js:30:34
which is this line of code const result = HelloWorld.Test(testArr, smin, smax);)
If I change redArray[i] = inputArray[i * 3]; to redArray[i] = ~(inputArray[i * 3]); to invert the color I'm getting two errors:
error C2675: unary '~': 'Napi::Value' does not define this operator or a
conversion to a type acceptable to the predefined operator
and
error C2088: '~': illegal for class
My Question
What is the correct way to implement my c-Function to work from node.js?
Thanks for any help!
The node.js team has created Array buffer example by using the node-addon-api (the C++ wrapper to N-API), it can be accessed from the following URL. https://github.com/nodejs/node-addon-examples/tree/master/array_buffer_to_native/node-addon-api
If you are looking for a pure N-API implementation (without using any wrapper) then you may take a look on the following examples, this has been created while I was learning the pure N-API.
https://github.com/msatyan/MyNodeC/blob/master/src/mync1/ArrayBuff.cpp.
This example covers the following scenario:
Receive an ArrayBuffer from JavaScript
Create an ArrayBuffer at native layer and return it to JavaScript
Create a TypedArray at native layer and return it to JavaScript
Create an ArrayBuffer at native layer with externally allocated memory
and return it to JavaScript
Sample JavaScript usage of it is available: https://github.com/msatyan/MyNodeC/blob/master/test/TestArrayBuff.js
If you are starting a new native add-on module, I may encourage to use CMake.js instead of node-gyp (look like node-gyp is ending its support). You can get more information about the modern project structure from a sample project that I have created during my N-API study. https://github.com/msatyan/MyNodeC
I am a little bit confused about how many threads .net core creates. I have a very simple program that fetch static contnet from our application`s server by array of urls:
class Program
{
static void Main(string[] args)
{
//ThreadPool.SetMaxThreads(10, 10);
var urls = new string[]
{
"http://url1.....",
"http://url2.....",
.......
};
var taskCount = 100;
var count = 1000;
var httpClient = new HttpClient {Timeout = TimeSpan.FromMilliseconds(10000)};
var tasks = new Collection<Task>();
var threads = new ConcurrentBag<int>();
for (var i = 0; i < taskCount; i++)
{
tasks.Add(Task.Run(async () =>
{
var sw = new Stopwatch();
for (var j = 0; j < count; j++)
{
foreach (var url in urls)
{
sw.Start();
try
{
threads.Add(Thread.CurrentThread.ManagedThreadId);
using (var r = await httpClient.GetAsync(url))
{
threads.Add(Thread.CurrentThread.ManagedThreadId);
if (r.StatusCode != HttpStatusCode.OK)
{
sw.Stop();
Console.WriteLine($"[{url}] - status: [{r.StatusCode}], time: [{sw.ElapsedMilliseconds}]");
}
else
{
using (var content = r.Content)
{
threads.Add(Thread.CurrentThread.ManagedThreadId);
_ = await content.ReadAsByteArrayAsync();
threads.Add(Thread.CurrentThread.ManagedThreadId);
sw.Stop();
Console.WriteLine($"[{url}] - status: [{r.StatusCode}], time: [{sw.ElapsedMilliseconds}]");
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
sw.Reset();
}
}
}));
}
Console.ReadKey();
File.AppendAllText(#"E:\temp\log.txt", $"t count: {threads.ToList().Distinct().ToList().Count}\r\n");
}
}
Only 100 tasks fetch content by urls, log this and do it again. When I run this program on OS Windows by using .NET Core 2.0, I see 10 threads in log file. I understand that these are threads from the thread pool, but when I look on count of threads in Windows Resource Monitor, I see 120 threads. Why?
If I run this program on OS Windows by using .NET Framework 4.7.1, I see 17 threads in log file and 37 threads in Windows Resource Monitor. If I use ThreadPool.SetMaxThreads(10, 10) for both cases, nothing changes, I still see 120 threads in .NET Core 2.0 and around 37 threads in .NET Framework 4.7.1 by looking on Windows Resource Monitor. I do not understand why. Please, someone explain to me what are these 120 threads and why does .NET Core 2.0 create them so much?
Those parameters can be seen in a Task Manager.
The answer maybe a bit huge but it will answer the entire question except for the DISK USAGE part.
Getting the CPU utilization in percentage(%)
Not sure if WMI can be used for this case but i prefer not to use WMI because not all the machines support WMI.I will try to use other classes such as PerformanceCounter or so for the solutions.Here's a sample code that will return the CPU utilization in %
public FinalResult as float;
public async void GetCPUCounter()
{
CounterSample firstValue = cpuCounter.NextSample();
await Task.Delay(900); //delay is required because first call will always return 0
CounterSample secondValue = cpuCounter.NextSample();
FinalResult = CounterSample.Calculate(firstValue, secondValue);
await Task.Delay(900);
GetCPUCounter(); //calling again to get repeated values
}
Now, just use a Windows.Forms.Timer to repeatedly get the cpu utilization.Before that, just call GetCPUCounter() once from anywhere in your code, let's say from Form_load event :
private void Form_load(...)
{
GetCPUCounter();
}
private void mytimer_Tick(....)
{
string cpuUsage = finalresult.ToString() + "%"
}
Getting the memory utilization in percentage(%)
Here's a full class with 2 async methods that will return both memory usage and total memory:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.VisualBasic.Devices;
public class Memory
{
public int TotalRamInMb;
public int TotalRamInGb;
public double UsedRam;
public int UsedRamPercentage;
public string GetTotalRam()
{
var CI = new ComputerInfo();
var mem = ulong.Parse(CI.TotalPhysicalMemory.ToString());
int Mb = Convert.ToInt16(mem / (double)(1024 * 1024));
TotalRamInMb = Mb;
if (Mb.ToString().Length <= 3)
return Mb.ToString() + " MB physical memory";
else
{
return (Convert.ToInt16(Mb / (double)1024)).ToString() + " GB physical memory";
TotalRamInGb = Convert.ToInt16(Mb / (double)1024);
}
}
public async void GetUsedRam()
{
double URam;
Process[] allProc = Process.GetProcesses();
foreach (var proc in allProc)
URam += ((proc.PrivateMemorySize64) / (double)(1024 * 1024));
UsedRam = URam;
UsedRamPercentage = (UsedRam * 100) / TotalRamInMb;
await Task.Delay(900);
GetUsedRam();
}
}
Get the values from the variables such as UsedRam, UsedRamPercentage,TotalRamInMb and so... And make sure to call GetUsedRam() once like we did with GetCPUCounter and then use a Forms.Timer to repeatedly get the values from the above variables.
I will update the answer soon by adding Disk usage retrieval process . Cheers!
I'm trying to understand one problem that I encountered recently in my project. I'm using Aurigma library to resize images. It is used in the single thread mode and produce only one thread during calculation. Lately I decided to move to ImageMagick project, because it is free and open source. I've built IM in the single thread mode and started to test. At first I wanted to compare their performance without interruptions, so I created a test that has high priorities for a process and its thread. Also, I set affinity to one core. I got that IM faster than Aurigma on ~25%. But than more threads I added than less IM had advantage against Aurigma.
My project is a windows service that starts about 7-10 child processes. Each process has 2 threads to process images. When I run my test as two different processes with 2 threads each, I noticed that IM worked worse than Aurigma on about 5%.
Maybe my question is not very detailed, but this scope is a little new for me and I would be glad to get direction for further investigation. How can it be that one program works faster if it is run on one thread in one process, but worse if it is run in multiple processes at the same time.
Fro example,
Au: 8 processes x 2Th(20 tasks per thread) = 320 tasks for 245 secs
IM: 8 processes x 2Th(20 tasks per thread) = 320 tasks for 280 secs
Au: 4 processes x 2Th(20 tasks per thread) = 160 tasks for 121 secs
IM: 4 processes x 2Th(20 tasks per thread) = 160 tasks for 141 secs
We can see that Au works better if we have more that 1 process, but in single process mode: Au process one task for 2,2 sec, IM for 1,4 sec and the sum time is better for IM
private static void ThreadRunner(
Action testFunc,
int repeatCount,
int threadCount
)
{
WaitHandle[] handles = new WaitHandle[threadCount];
var stopwatch = new Stopwatch();
// warmup
stopwatch.Start();
for (int h = 0; h < threadCount; h++)
{
var handle = new ManualResetEvent(false);
handles[h] = handle;
var thread = new Thread(() =>
{
Runner(testFunc, repeatCount);
handle.Set();
});
thread.Name = "Thread id" + h;
thread.IsBackground = true;
thread.Priority = ThreadPriority.Normal;
thread.Start();
}
WaitHandle.WaitAll(handles);
stopwatch.Stop();
Console.WriteLine("All Threads Total time taken " + stopwatch.ElapsedMilliseconds);
}
private static void Runner(
Action testFunc,
int count
)
{
//Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2); // Use only the second core
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.BelowNormal;
Process.GetCurrentProcess().PriorityBoostEnabled = false;
Thread.CurrentThread.Priority = ThreadPriority.Normal;
var stopwatch = new Stopwatch();
// warmup
stopwatch.Start();
while(stopwatch.ElapsedMilliseconds < 10000)
testFunc();
stopwatch.Stop();
long elmsec = 0;
for (int i = 0; i < count; i++)
{
stopwatch.Reset();
stopwatch.Start();
testFunc();
stopwatch.Stop();
elmsec += stopwatch.ElapsedMilliseconds;
Console.WriteLine("Ticks: " + stopwatch.ElapsedTicks +
" mS: " + stopwatch.ElapsedMilliseconds + " Thread name: " + Thread.CurrentThread.Name);
}
Console.WriteLine("Total time taken " + elmsec + " Thread name: " + Thread.CurrentThread.Name);
}
/// <summary>
/// Entry point
/// </summary>
/// <param name="args"></param>
private static void Main(string[] args)
{
var files = GetFiles(args.FirstOrDefault());
if (!files.Any())
{
Console.WriteLine("Source files were not found.");
goto End;
}
//// Run tests
Console.WriteLine("ImageMagick run... Resize");
Runner(() => PerformanceTests.RunResizeImageMagickTest(files[0]), 20);
Console.WriteLine("Aurigma run... Resize");
Runner(() => PerformanceTests.RunResizeAurigmaTest(files[0]), 20);
Console.WriteLine("ImageMagick run... multi Resize");
ThreadRunner(() => PerformanceTests.RunResizeImageMagickTest(files[0]), 20, 2);
Console.WriteLine("Aurigma run... multi Resize");
ThreadRunner(() => PerformanceTests.RunResizeAurigmaTest(files[0]), 20, 2);
End:
Console.WriteLine("Done");
Console.ReadKey();
}
public static void RunResizeImageMagickTest(string source)
{
float[] ratios = { 0.25f, 0.8f, 1.4f };
// load the source bitmap
using (MagickImage bitmap = new MagickImage(source))
{
foreach (float ratio in ratios)
{
// determine the target image size
var size = new Size((int)Math.Round(bitmap.Width * ratio), (int)Math.Round(bitmap.Height * ratio));
MagickImage thumbnail = null;
try
{
thumbnail = new MagickImage(bitmap);
// scale the image down
thumbnail.Resize(size.Width, size.Height);
}
finally
{
if (thumbnail != null && thumbnail != bitmap)
{
thumbnail.Dispose();
}
}
}
}
}
public static void RunResizeAurigmaTest(string source)
{
float[] ratios = { 0.25f, 0.8f, 1.4f };
//// load the source bitmap
using (ABitmap bitmap = new ABitmap(source))
{
foreach (float ratio in ratios)
{
// determine the target image size
var size = new Size((int)Math.Round(bitmap.Width * ratio), (int)Math.Round(bitmap.Height * ratio));
ABitmap thumbnail = null;
try
{
thumbnail = new ABitmap();
// scale the image down
using (var resize = new Resize(size, InterpolationMode.HighQuality))
{
resize.ApplyTransform(bitmap, thumbnail);
}
}
finally
{
if (thumbnail != null && thumbnail != bitmap)
{
thumbnail.Dispose();
}
}
}
}
}
Code for testing is added. I use C#/.NET, ImageMagick works through ImageMagick.Net library, for Aurigma there is one too. For IM .net lib is written on C++/CLI, IM is C. A lot of languages are used.
OpenMP for IM is off.
Could be a memory cache issue. It is possible that multiple threads utilizing memory in a certain way create a scenario that one thread invalidates cache memory that another thread was using, causing a stall.
Programs that are not purely number crunching, but rely on a lot of IO (CPU<->Memory) are more difficult to analyze.
I have a program which has a Ui with which users choose the way to display and do small configurations. It also has a background procedure, which continuously reads data from the network and update the data to display.
Now I put them in one process:
background procedure:
STATE MainWindow::Rcv()
{
DeviceMAP::iterator dev;
for(dev= dev_map.begin(); dev!= dev_map.end(); dev++)
{
dev->second.rcvData();//receive data from the network, the time can be ignored.
BitLog* log = new BitLog();
dev->second.parseData(log);
LogItem* logItem = new LogItem();
logItem->time = QString::fromLocal8Bit(log->rcvTime.c_str());
logItem->name = QString::fromLocal8Bit(log->basicInfo.getName().c_str());
logItem->PIN = QString::fromLocal8Bit(log->basicInfo.getPIN().c_str()).toShort();
delete log;
add_logItem(logItem);
}
return SUCCESS;
}
add_logItem:
void MainWindow::add_logItem(LogItem* logItem)
{
writeToFile(logItem);
Device* r = getDevbyPIN(QString::number(logItem->PIN));
if(r == NULL)return;
devInfo_inside_widget::States state = logItem->state;
bool bool_list[portsNum_X];
for(int i =0; i < portsNum_X; i++)
{
bool_list[i] = 0;
}
for(int i = 0; i < portsNum; i++)
{
bool_list[i] = (logItem->BITS[i/8] >> (7 - i%8)) & 0x1;
}
r->refresh(state, logItem->time, bool_list);//update data inside...state, time , BITS...
IconLabel* icl = getIConLabelByDev(r);//update data
icl->refresh(state);
logDisplayQueue.enqueue(logItem);//write queue here
int size = logDisplayQueue.size();
if(size > 100)
{
logDisplayQueue.dequeue();//write queue here
}
}
The section above has not dealt with any ui operations yet, but when user push a radio button in the ui, the program has to filter the data in the queue and display it in the table widget:
ui operations:
void MainWindow::filter_log_display(bool bol)
{
row_selectable = false;
ui->tableWidget->setRowCount(0);//delete table items all
row_selectable = true;
int size_1 = logDisplayQueue.size() - 1;
ui->tableWidget->verticalScrollBar()->setSliderPosition(0);
if(size_1+1 < 100)
{
ui->tableWidget->setRowCount(size_1 + 1);
}
else
{
ui->tableWidget->setRowCount(100);//display 100 rows at most
}
if(bol)//filter from logDisplayQueue and display unworking-state-log rows
{
int index = 0;
for(int queue_i = size_1; queue_i >= 0; queue_i--)
{
LogItem* logItem = (LogItem*)logDisplayQueue.at(queue_i); // read queue here
if(logItem->state == STATE_WORK || logItem->state == STATE_UN)continue;
QString BITS_str = bits2Hexs(logItem->BITS);
ui->tableWidget->setItem(index, 0, new QTableWidgetItem(logItem->time));//time
ui->tableWidget->setItem(index, 1, new QTableWidgetItem(logItem->name));//name
ui->tableWidget->setItem(index, 2, new QTableWidgetItem(BITS_str));//BITS
if(queue_i == oldRowItemNo)ui->tableWidget->selectRow(index);
index++;
}
ui->tableWidget->setRowCount(index);
}
else//display all rows
{
for(int queue_i = size_1, index = 0; queue_i >= 0; queue_i--, index++)
{
LogItem* logItem = (LogItem*)logDisplayQueue.at(queue_i); //read queue here
QString BITS_str = bits2Hexs(logItem->BITS);//
finish = clock();
ui->tableWidget->setItem(index, 0, new QTableWidgetItem(logItem->time));//time
ui->tableWidget->setItem(index, 1, new QTableWidgetItem(logItem->name));//name
ui->tableWidget->setItem(index, 2, new QTableWidgetItem(BITS_str));//BITS
if(queue_i == oldRowItemNo)ui->tableWidget->selectRow(index);
}
}
}
So the queue is quite samll and the background procedure is quite frequent(nearly 500 times per sec). That is, the queue will be written 500 times in 1 sec, but displayed time from time by the user.
I want to split the functions into two threads and run them together, one rev and update data, one display.
If i do not use any lock or mutex, the user may get the wrong data, but if i force the write-data procedure enter critical section and leave critical section everytime, it will be a heavy overload. :)
Should I use CRITICAL_SECTION or something else, any suggestions related?(my words could be verbose for you :) , i only hope for some hints :)
I'd put "Recv" function in another QObject derived class, put it under other QThread not main gui thread and connect "logItemAdded(LogItem* item)" signal to main window's "addLogItem(LogItem* item)" slot.
for just quick and dirty hint my conceptual code follows.
#include <QObject>
class Logger : public QObject
{
Q_OBJECT
public:
Logger(QObject* parent=0);
virtual ~Logger();
signals:
void logItemAdded(LogItem* logItem);
public slots:
protected:
void Rcv()
{
// ...
// was "add_logItem(logItem)"
emit logItemAdded(logItem);
}
};
MainWindow::MainWindow(...)
{
Logger logger = new Logger;
// setup your logger
QThread* thread = new QThread;
logger->moveToThread(thread);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
Hope this helps.
Good luck.