Possible bug: CPU usage drop down after a long time running in multi-threads situation

Post any defects you find in the released or beta versions of the ImageMagick software here. Include the ImageMagick version, OS, and any command-line required to reproduce the problem. Got a patch for a bug? Post it here.
Post Reply
Jackie Lee
Posts: 5
Joined: 2017-09-11T00:20:13-07:00
Authentication code: 1151

Possible bug: CPU usage drop down after a long time running in multi-threads situation

Post by Jackie Lee » 2017-09-11T03:15:58-07:00

When using the 7 threads in a 24 cores machine, the CPU usage would become really low after running a long time. It is about 400% at first, could become 200% in the final.
Here is my test program, I run it in CentOS 6.5 several times and the problem occured everytime. the ImageMagick version is 6.9.8.

Code: Select all

#include <stdio.h>
#include <fstream>
#include <iostream>
#include <string>
#include <pthread.h>
#include <wand/magick_wand.h>
void ReadJpgFile(const char *filename, unsigned char *&buff, size_t &size)
{
  std::ifstream infile;
  infile.open(filename);
  infile.seekg(0, std::ios::end);
  size = infile.tellg();
  infile.seekg(0, std::ios::beg);
  buff = new unsigned char[size];
  infile.read((char *)buff, size);
  infile.close();
}
void WriteJpgFile(const char *filename, unsigned char *buff, size_t size)
{
  std::ofstream fout; // create an ofstream object named fout
  fout.open(filename,std::ios::trunc);
  fout.write((char *)buff,size);
  fout.close();
}
void CompressJpgUsingImageMagick(unsigned char *orig_buff, unsigned char *&buff, size_t &img_size)
{
  MagickWand *im = NULL;
  im = NewMagickWand();
  MagickReadImageBlob(im, (const unsigned char *)orig_buff, img_size);
  MagickResetIterator(im);
  MagickSetImageCompressionQuality(im, 50);
  double factors420[3] = {2.0, 1.0, 1.0};
  MagickSetSamplingFactors(im, 3, factors420);
  //MagickSetInterlaceScheme(im, LineInterlace);
  MagickSetImageFormat(im, "jpg");
  MagickStripImage(im);
  if(buff != NULL) free(buff);
  img_size = 0;
  buff = (unsigned char *)MagickGetImageBlob(im, &img_size);
  DestroyMagickWand(im);
}
void* WholeJpegCompress(void*)
{
  unsigned char *orig_buff = NULL;
  unsigned char *buff = NULL;
  size_t ori_img_size = 0;
  size_t img_size = 0;
  for(int i = 0; i < 1; i++)
  {
    ReadJpgFile("1.jpg",orig_buff,ori_img_size);
    for(int j = 0; j < 5000000; j++)
    {
      img_size = ori_img_size;
      if(buff != NULL)
      {
        free(buff);
        buff = NULL;
      }
      CompressJpgUsingImageMagick(orig_buff,buff,img_size);
    }
    //WriteJpgFile("2.jpg",buff,img_size);
    printf("outsize: %d\n",img_size);
    free(buff);
    buff = NULL;
    delete orig_buff;
    orig_buff = NULL;
  }
  pthread_exit(0);
  return NULL;
}
void* EmptyRun(void*)
{
  int i = 0;
  while(i < 1000000000)
  {
    int j = 0;
    while(j < 100) j++;
    i++;
  }
  pthread_exit(0);
}
int main()
{
  pthread_t deal_thr[7];
  for(int i = 0; i < 7; i ++)
  {
    int ret=pthread_create(&deal_thr[i],NULL,WholeJpegCompress,NULL);
    if(ret!=0)
    {
      printf("Create pthread error!\n");
      return -1;
    }
  }
  for(int i = 0; i < 7; i ++)
  {
    pthread_join(deal_thr[i],NULL);
  }
  return 0;
}

Jackie Lee
Posts: 5
Joined: 2017-09-11T00:20:13-07:00
Authentication code: 1151

Re: Possible bug: CPU usage drop down after a long time running in multi-threads situation

Post by Jackie Lee » 2017-09-12T19:59:08-07:00

I found the bug now. A thread safe issue in MagickWand/wand.c.

Code: Select all

WandExport size_t AcquireWandId(void)
{
  static size_t
    id = 0;

  if (wand_semaphore == (SemaphoreInfo *) NULL)
    ActivateSemaphoreInfo(&wand_semaphore);
  LockSemaphoreInfo(wand_semaphore);
  if (wand_ids == (SplayTreeInfo *) NULL)
    wand_ids=NewSplayTree((int (*)(const void *,const void *)) NULL,
      (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
  id++;
  (void) AddValueToSplayTree(wand_ids,(const void *) id,(const void *) id);
  instantiate_wand=MagickTrue;
  UnlockSemaphoreInfo(wand_semaphore);
  return(id);
}
the id would be changed before return when there are multi-threads.
there is also a performance bug in 
WandExport void RelinquishWandId(const size_t id)
{
  LockSemaphoreInfo(wand_semaphore);
  if (wand_ids != (SplayTreeInfo *) NULL)
    (void) DeleteNodeByValueFromSplayTree(wand_ids,(const void *) id);
  UnlockSemaphoreInfo(wand_semaphore);
}
It would be better to use DeleteNodeFromSplayTree rather than DeleteNodeByValueFromSplayTree
I would create a pull request on github soon.


Jackie Lee
Posts: 5
Joined: 2017-09-11T00:20:13-07:00
Authentication code: 1151

Re: Possible bug: CPU usage drop down after a long time running in multi-threads situation

Post by Jackie Lee » 2017-09-12T22:56:33-07:00

Yeah, the #750 and #751 pull request is created by me.

Post Reply