Почему Perl Inline :: C сортирует 4.0e-5 после 4.4e-5? - PullRequest
3 голосов
/ 30 апреля 2009

Я создал модуль Perl Inline :: C , но с сортировкой есть некоторая странность. Кто-нибудь знает, почему это будет так? Почему 4.0e-5 не первый?

my $ref = [ 5.0e-5,4.2e-5,4.3e-5,4.4e-5,4.4e-5,4.2e-5,4.2e-5,4.0e-5]; 

use Inline C => <<'END_OF_C_CODE';

void test(SV* sv, ...) {

  I32 i;
  I32 arrayLen;
  AV* data;
  float retval;
  SV** pvalue;

  Inline_Stack_Vars;
  data = SvUV(Inline_Stack_Item(0));

  /* Determine the length of the array */
  arrayLen = av_len(data);

  // sort 
  sortsv(AvARRAY(data),arrayLen+1,Perl_sv_cmp_locale);

  for (i = 0; i < arrayLen+1; i++) {

    pvalue = av_fetch(data,i,0);  /* fetch the scalar located at i .*/
    retval = SvNV(*pvalue);  /* dereference the scalar into a number. */

    printf("%f \n",newSVnv(retval));
  }
}

END_OF_C_CODE

тест ($ исх);

0,000042
0.000042
0.000042
0.000043
0.000044
0.000044
0.000040
0.000050

Ответы [ 3 ]

4 голосов
/ 30 апреля 2009

Поскольку вы сортируете лексически, попробуйте этот код:

#!/usr/bin/perl

use strict;
use warnings;

my $ref = [ 5.0e-5,4.2e-5,4.3e-5,4.4e-5,4.4e-5,4.2e-5,4.2e-5,4.0e-5]; 

print "Perl with cmp\n";
for my $val (sort @$ref) {
    printf "%f \n", $val;
}

print "Perl with <=>\n";
for my $val (sort { $a <=> $b } @$ref) {
    printf "%f \n", $val;
}

print "C\n";

test($ref);

use Inline C => <<'END_OF_C_CODE';

void test(SV* sv, ...) {

  I32 i;
  I32 arrayLen;
  AV* data;
  float retval;
  SV** pvalue;

  Inline_Stack_Vars;
  data = SvUV(Inline_Stack_Item(0));

  /* Determine the length of the array */
  arrayLen = av_len(data);

  // sort 
  sortsv(AvARRAY(data),av_len(data)+1,Perl_sv_cmp_locale);

  arrayLen = av_len(data);
  for (i = 0; i < arrayLen+1; i++) {

    pvalue = av_fetch(data,i,0);  /* fetch the scalar located at i .*/
    retval = SvNV(*pvalue);  /* dereference the scalar into a number. */

    printf("%f \n",newSVnv(retval));
  }
}

END_OF_C_CODE

Конечно, лексически 0.00040 меньше, чем 0.00042, но вы не сравниваете 0.00040 с 0.00042; вы сравниваете число 0.00040, преобразованное в строку, с числом 0.00042, преобразованным в строку. Когда число становится слишком большим или маленьким, строковая логика Perl прибегает к использованию научных обозначений. Итак, вы сортируете набор строк

"4.2e-05", "4.2e-05", "4.2e-05", "4.3e-05", "4.4e-05", "4.4e-05", "4e-05", "5e-05"

, которые правильно отсортированы. Perl радостно превращает эти строки в их числа, когда вы просите об этом в формате %f в printf. Вы можете сами упорядочить числа, но поскольку вы заявили, что хотите, чтобы это было быстрее, это было бы ошибкой. Вы не должны пытаться оптимизировать программу, прежде чем узнаете, где она медленная (преждевременная оптимизация - корень всех зол *). Напишите свой код, затем запустите Devel :: NYTProf против него, чтобы найти, где он медленный. Если необходимо, перепишите эти части в XS или Inline::C (я предпочитаю XS). Вы обнаружите, что вы выбираете правильную структуру данных быстрее, чем микрооптимизации, подобные этой.

* Кнут, Дональд . Структурированное программирование с переходом к операторам , ACM Journal Computing Surveys , том 6, № 4, декабрь 1974. с.268.

2 голосов
/ 30 апреля 2009

Perl_sv_cmp_locale - ваша функция сортировки, которая, я подозреваю, является лексическим сравнением. Ищите числовую сортировку или напишите свою собственную.

1 голос
/ 02 мая 2009

Получить ответ с помощью людей на http://www.perlmonks.org/?node_id=761015

Я провел некоторое профилирование (DProf), и это в 4 раза улучшило скорость

Общее прошедшее время = 0,543205 секунд
Пользователь + Системное время = 0,585454 секунды
Эксклюзивные времена
% Времени исключено CumulS # Звонки в секунду / Csec / c Имя
100. 0,590 0,490 100000 0,0000 0,0000 test_inline_c_pkg :: процентов2

Общее прошедшее время = 2,151647 секунд
Пользователь + Системное время = 1,991647 секунд
Эксклюзивные времена
% Времени исключено CumulS # Вызовы сек / вызов Csec / c Имя
104. 2,080 1,930 100000 0,0000 0,0000 основной :: процентов2

Вот код

use Inline C => <<'END_OF_C_CODE';

#define SvSIOK(sv) ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV)) == SVf_IOK)
#define SvNSIV(sv) (SvNOK(sv) ? SvNVX(sv) : (SvSIOK(sv) ? SvIVX(sv) : sv_2nv(sv)))

static I32 S_sv_ncmp(pTHX_ SV *a, SV *b) {

  const NV nv1 = SvNSIV(a);
  const NV nv2 = SvNSIV(b);
  return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0;
}

void test(SV* sv, ...) {

  I32 i;
  I32 arrayLen;
  AV* data;
  float retval;
  SV** pvalue;

  Inline_Stack_Vars;
  data = SvUV(Inline_Stack_Item(0));

  /* Determine the length of the array */
  arrayLen = av_len(data);

  /* sort descending (send numerical sort function S_sv_ncmp) */
  sortsv(AvARRAY(data),arrayLen+1, S_sv_ncmp);

  for (i = 0; i < arrayLen+1; i++) {

    pvalue = av_fetch(data,i,0);  /* fetch the scalar located at i .*/
    retval = SvNV(*pvalue);  /* dereference the scalar into a number. */

    printf("%f \n",newSVnv(retval));
  }
}

END_OF_C_CODE
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...