年末の帰省チケットを解約したとき(2020年 12月 17日の日記参照)に価格が激変していたことが気になったので、年末年始の羽田→千歳便の各チケットの価格をプロットしてみました。
どうやらチケットの種類に関係なく、最後に付いているアルファベットで価格帯が決まるようです。A が一番高くて、B, C, D, ... と安くなっていくようです。10月に予約した SUPER VALUE 75 H が、今日予約できる VALUE 3 J より高くなるのはこれが理由でした。
ANA の予約システムは、遠い予約日(1/M くらい)だと、空席予測を強気に出すのかやや割高の運賃設定をしています。
くらいかな?早朝便、深夜便などは 1ランク安くなりがちです。しかし、搭乗日が近づいて(増発を決めてしまった 12/30 など)、誰も乗らないことに気づき始めると、未だかつて見たことない安さのチケットが出現します。
こんな感じですね。安いなあ。
遠い予約日が強気の価格になるのは、SUPER VALUE でも傾向が一緒のようです。SUPER VALUE の予約日は最短でも 21日後と、必然的に遠くなるため、COVID-19 の状況下ですと、予約日が遠い SUPER VALUE の方がかえって割高で買ってしまう可能性が高いです。
例えば、一番近い SUPER VALUE の予約日は 21日後の 1/10 です。ラインナップは VALUE 3 H, VALUE 1 G, SUPER VALUE 21 J 辺りで、今この瞬間は SUPER VALUE 21 J が一番安く見えますが、1/10 はおそらく誰も乗りません(帰省ラッシュがない以上、U ターンラッシュも起きない)から、1/3 くらいまで待てば、VALUE 3 K とかが登場して、SUPER VALUE 21 J の価格を下回ると思います。
しかも今は「あんしん変更キャンペーン」があるので、SUPER VALUE 21 J で予約して払い込んでしまい、年始に安い便があれば切り替え、払い戻しを受ければノーリスクで安く乗れるはずです。
未だかつて年末の羽田→千歳便がこんな低価格で投げ売りされたことはありません。どれだけぼったくっても、皆が渋々乗るので「ドル箱路線」と称されたほどです。
10年来、散々、羽田→千歳便でボラれてきた身としては、今年は流石の ANA もぼったくりはできなかったか……と思いました。けどまあ COVID-19 に関しては同情しかなくて、乗れる機会があったら飛行機乗って応援したいですね。九州辺りに旅行に行きたいな〜。
「あんしん変更キャンペーン」はただでさえ客足が遠のく中、安く乗られ、割安で解約され、ANA としては散々でしょう。
逆にこっちはあまり同情してません。飛行機の割引制度は縛りが多くて理不尽です。今の方が素直だし普通です。是非、このまま続けて欲しいですね。
メモ: 技術系?の話は Facebook から転記しておくことにした。いろいろ修正。
車に乗ってるとたまに疑問に思うんですが、燃費ってアクセル開度で決まるんですかね?
同じ勾配、同じアクセル開度なら、毎速 2500rpm で変速しても、2速 5000rpm まで引っ張って、すぐ 3, 4, 5 速と変速しても、燃料消費量は同じ??なんてことを Facebook に書いてみたところ、会社の同僚のみなさんから色々教えていただけました。
同じアクセル開度なら、エンジンの燃焼効率の最適点が一番燃費が良いので、1速でぶん回すより、4速くらいの方が一般的には燃費が良いはず、とのことです。レスが超早かったです。さすが車の専門家達の会社だなと痛感しますね。
通常、燃焼効率の最適点は公開されていません。代わりとしてエンジン出力の最大点を使おうと思います。燃焼効率最適点=燃費の最大点、エンジン出力の最大点=加速の最大点であり、この 2つは一致しません。ただし、効率が悪いにも関わらず最大出力に達するのは、ちょっと考えにくいですし、一致はしなくてもさほど遠くないだろう、という目論見です。
スバルの EJ20 エンジンはいろんな車種に乗っていて、自分のレガシィ B4 のエンジン性能曲線が探せませんでした……。インプレッサ WRX STI の EJ20 エンジン性能曲線はすぐに出てきた(パワーユニット : ドライビング - WRX STI - SUBARU)ので、これを参考にしようと思います。
もちろん
辺りはわかってます。
が、まあ、ざっくり言って、トルクカーブは 2500〜4500rpm くらいまでピークかつ真っ平らです。ほぼどこで変速しても大丈夫!素晴らしいエンジンですね〜。
2000rpm 前後の加速が眠くて、突然速くなる感じがするのも、レガシィが重たいからだと思いこんでいたんですけど、2000〜2500rpm でトルクが急激に立ち上がるエンジン特性からくるものなんですね。見覚えあるなと思って調べたら、昔も同じことを言っていました(2010年 9月 5日の日記、2010年 9月 7日の日記参照)。完全に忘れてました。
何年乗ってるんだ、今更かよって感想ですけど……。
Facebook で教えていただいたところによると、EJ20 は OBD2 端子(On-board diagnostics という故障診断の信号を送受信できる端子)から燃料消費量の情報が取れるはずなので、それを見たらどうか?ELM327 互換のアダプタで Bluetooth でスマホにデータを送る手もある、という素晴らしいアイデアをいただきました。
昔に付けたメーター(2011年 5月 4日の日記参照)で、瞬間燃費が表示できないか試してみようかな。
メモ: 技術系?の話は Facebook から転記しておくことにした。大幅に追記。
実家と話し合って、今年末の北海道への帰省を取りやめました。年末がクッソ暇になりました。
帰省用の飛行機チケットを解約したんですが、ANA が最近やってる「あんしん変更キャンペーン」(ANA 公式サイト)により、運賃や解約ルールが意味不明になってます。
買ったチケットは、
計 95,040円です。
解約の仕方をいくつか試したところ、払戻金額の見積もりがべらぼうに変わり驚きました。
各パターンで何が起きているのでしょう?個人的な予想に過ぎませんが、
バリュー系チケットの値段は需要連動(ANA の解説)らしく、11月のコロナ再流行で需要が急激に落ち込み、スーパーバリュー75H とバリュー 3J の逆転現象が発生したのでしょう。この需要連動という想定そのものが「直前に需要が急減する」という事態を想定していない節がありますね。
もし 10月くらいにスーパーバリュー系を予約した人は、1万円単位で大損してるはずです。年末に飛行機に乗る人は要確認です。
今の ANA の一番良くないところは、普通に解約すると 5万円近く大損するところですが、ANA の QA を見る限り「コロナ関連で解約の特別対応はしない、所定のキャンセル料を払え」とるのでANA としてはパターン 1 の解約で ANA に 5万円寄付せよ、という意思なんでしょうか?
私は、パターン 3 を試してみたので、手数料含めて 2,500円くらいで解約できちゃったように見えます。
予約システムのバグだったんですかね?ちゃんと払い戻しされるかなあ??不安だわあ……。
メモ: 技術系?の話は Facebook から転記しておくことにした。
前半では ztest_test_suite() の実装を見ました。戻って見るのは面倒だと思うので ztest の使い方の例を再掲しておきます。
// zephyr/tests/lib/sprint/src/main.c
void test_main(void)
{
ztest_test_suite(test_sprintf, //★変数名★
ztest_unit_test(test_sprintf_double), //★テスト 1つ目★
ztest_unit_test(test_sprintf_integer), //★テスト 2つ目★
ztest_unit_test(test_vsprintf),
ztest_unit_test(test_vsnprintf),
ztest_unit_test(test_sprintf_string),
ztest_unit_test(test_sprintf_misc));
ztest_run_test_suite(test_sprintf);
}
後半の ztest_run_test_suite() マクロの実装を見ます。
// zephyr/subsys/testsuite/ztest/include/ztest_test.h
/**
* @brief Run the specified test suite.
*
* @param suite Test suite to run.
*/
#define ztest_run_test_suite(suite) \
z_ztest_run_test_suite(#suite, _##suite)
// zephyr/subsys/testsuite/ztest/src/ztest.c
void z_ztest_run_test_suite(const char *name, struct unit_test *suite)
{
int fail = 0;
if (test_status < 0) {
return;
}
init_testing(); //★何もしない★
PRINT("Running test suite %s\n", name);
PRINT_LINE;
while (suite->test) {
fail += run_test(suite); //★テストを実行★
suite++;
if (fail && FAIL_FAST) {
break;
}
}
if (fail) {
TC_PRINT("Test suite %s failed.\n", name);
} else {
TC_PRINT("Test suite %s succeeded\n", name);
}
test_status = (test_status || fail) ? 1 : 0;
}
#ifndef KERNEL
...
#else
...
static int run_test(struct unit_test *test)
{
int ret = TC_PASS;
TC_START(test->name);
//★テスト関数 1つに対し、1つスレッドを作る★
k_thread_create(&ztest_thread, ztest_thread_stack,
K_THREAD_STACK_SIZEOF(ztest_thread_stack),
(k_thread_entry_t) test_cb, (struct unit_test *)test,
NULL, NULL, CONFIG_ZTEST_THREAD_PRIORITY,
test->thread_options | K_INHERIT_PERMS,
K_NO_WAIT);
k_thread_name_set(&ztest_thread, "ztest_thread");
k_thread_join(&ztest_thread, K_FOREVER);
phase = TEST_PHASE_TEARDOWN;
test->teardown();
phase = TEST_PHASE_FRAMEWORK;
...
#endif /* !KERNEL */
関数 run_test() は非常に特徴的で、ztest では各ユニットテストを実行する際に、専用のスレッドを生成する仕組みになっています。浮動小数点数命令が Illegal Instruction 例外になってしまうのは、この仕組みが原因です。
勘の良い人は Zephyr のドキュメント(k_thread_create() へのリンク)を見ただけで、何が悪いかわかるかも。
手掛かりは API の options 引数が test->thread_options | K_INHERIT_PERMS になっていることです。前半で説明したとおり test->thread_options = 0 であり、K_INHERIT_PERMS 以外のオプションは指定されません。スレッド内で浮動小数点数命令を使いたいなら K_FP_REGS の指定が要るのでは?と思った方、その通りです。大正解。
K_FP_REGS が答えですよと言われても、何だそれ?と思うほうが普通です(私もそうでした)。Zephyr のスレッド生成関数をざっと追いかけましょう。
// (build_dir)/zephyr/include/generated/syscalls/kernel.h
static inline k_tid_t k_thread_create(struct k_thread * new_thread, k_thread_stack_t * stack, size_t stack_size, k_thread_entry_t entry, void * p1, void * p2, void * p3, int prio, uint32_t options, k_timeout_t delay)
{
#ifdef CONFIG_USERSPACE
if (z_syscall_trap()) {
uintptr_t more[] = {
*(uintptr_t *)&p2,
*(uintptr_t *)&p3,
*(uintptr_t *)&prio,
*(uintptr_t *)&options,
*(uintptr_t *)&delay
};
return (k_tid_t) arch_syscall_invoke6(*(uintptr_t *)&new_thread, *(uintptr_t *)&stack, *(uintptr_t *)&stack_size, *(uintptr_t *)&entry, *(uintptr_t *)&p1, (uintptr_t) &more, K_SYSCALL_K_THREAD_CREATE);
}
#endif
compiler_barrier();
//★今回、ユーザー空間は未使用なので、引数を同じ順で渡すだけ★
return z_impl_k_thread_create(new_thread, stack, stack_size, entry, p1, p2, p3, prio, options, delay);
}
// zephyr/kernel/thread.c
#ifdef CONFIG_MULTITHREADING
k_tid_t z_impl_k_thread_create(struct k_thread *new_thread,
k_thread_stack_t *stack,
size_t stack_size, k_thread_entry_t entry,
void *p1, void *p2, void *p3,
int prio, uint32_t options, k_timeout_t delay)
{
__ASSERT(!arch_is_in_isr(), "Threads may not be created in ISRs");
/* Special case, only for unit tests */
#if defined(CONFIG_TEST) && defined(CONFIG_ARCH_HAS_USERSPACE) && !defined(CONFIG_USERSPACE)
__ASSERT((options & K_USER) == 0,
"Platform is capable of user mode, and test thread created with K_USER option,"
" but neither CONFIG_TEST_USERSPACE nor CONFIG_USERSPACE is set\n");
#endif
z_setup_new_thread(new_thread, stack, stack_size, entry, p1, p2, p3,
prio, options, NULL); //★スレッドの情報初期化★
if (!K_TIMEOUT_EQ(delay, K_FOREVER)) {
schedule_new_thread(new_thread, delay);
}
return new_thread;
}
ここまでは入り口です。ユーザー空間を使わない限り、多少チェックが入っているくらいで、ほぼ素通りします。
/*
* The provided stack_size value is presumed to be either the result of
* K_THREAD_STACK_SIZEOF(stack), or the size value passed to the instance
* of K_THREAD_STACK_DEFINE() which defined 'stack'.
*/
char *z_setup_new_thread(struct k_thread *new_thread,
k_thread_stack_t *stack, size_t stack_size,
k_thread_entry_t entry,
void *p1, void *p2, void *p3,
int prio, uint32_t options, const char *name)
{
char *stack_ptr;
...
z_waitq_init(&new_thread->base.join_waiters);
/* Initialize various struct k_thread members */
z_init_thread_base(&new_thread->base, prio, _THREAD_PRESTART, options); //★スレッド生成(共通部分)★
stack_ptr = setup_thread_stack(new_thread, stack, stack_size);
#ifdef KERNEL_COHERENCE
/* Check that the thread object is safe, but that the stack is
* still cached!
*/
__ASSERT_NO_MSG(arch_mem_coherent(new_thread));
__ASSERT_NO_MSG(!arch_mem_coherent(stack));
#endif
arch_new_thread(new_thread, stack, stack_ptr, entry, p1, p2, p3); //★スレッドの生成(アーキテクチャ依存の処理)★
/* static threads overwrite it afterwards with real value */
new_thread->init_data = NULL;
new_thread->fn_abort = NULL;
#ifdef CONFIG_USE_SWITCH
/* switch_handle must be non-null except when inside z_swap()
* for synchronization reasons. Historically some notional
* USE_SWITCH architectures have actually ignored the field
*/
__ASSERT(new_thread->switch_handle != NULL,
"arch layer failed to initialize switch_handle");
#endif
...
void z_init_thread_base(struct _thread_base *thread_base, int priority,
uint32_t initial_state, unsigned int options)
{
/* k_q_node is initialized upon first insertion in a list */
thread_base->user_options = (uint8_t)options; //★オプションは test->thread_options (= 0) | K_INHERIT_PERMS (= 8) ★
thread_base->thread_state = (uint8_t)initial_state;
thread_base->prio = priority;
thread_base->sched_locked = 0U;
#ifdef CONFIG_SMP
thread_base->is_idle = 0;
#endif
/* swap_data does not need to be initialized */
z_init_thread_timeout(thread_base);
}
// zephyr/arch/riscv/core/thread.c
void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack,
char *stack_ptr, k_thread_entry_t entry,
void *p1, void *p2, void *p3)
{
struct __esf *stack_init;
...
#if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)
if ((thread->base.user_options & K_FP_REGS) != 0) { //★この条件に引っかからず、浮動小数点数演算機能が有効にならない★
stack_init->mstatus |= MSTATUS_FS_INIT;
}
stack_init->fp_state = 0;
#endif
stack_init->mepc = (ulong_t)z_thread_entry_wrapper;
...
アーキテクチャ依存処理 arch_new_thread() にやっと K_FP_REGS が出現します。user_options は z_init_thread_base() で設定しているとおり、API の引数 options の値そのものです。
引数 options に K_FP_REGS を指定しない場合、mstatus CSR の FS フィールドが設定されず 0 のままになります。RISC-V の仕様では、ハードウェアが浮動小数点数命令をサポートしていても、mstatus の FS フィールドが 0 だと Illegal Instruction 例外を発生させます。QEMU も当然この仕様に習った実装になっています(2020年 12月 10日の日記参照)。
以上が ztest で浮動小数点数命令を使うと例外が発生する原因です。原因はわかりましたが、スマートな直し方がわからないので、修正に関しては保留中です。
最初の方で init_testing(); //★何もしない★ とだけ書いてスルーしてしまった部分がありましたので、実装を載せておきます。
// zephyr/subsys/testsuite/ztest/src/ztest.c
#ifndef KERNEL
...
#else
...
static void init_testing(void)
{
k_object_access_all_grant(&ztest_thread);
}
#endif /* !KERNEL */
// zephyr/(build_dir)/zephyr/misc/generated/syscalls_links/include/sys/kobject.h
static inline void k_object_access_all_grant(const void *object)
{
ARG_UNUSED(object);
}
この通り、何もしていません。
Zephyr のテストに用いられる ztest というフレームワークがあります。使い方は、下記の通りで、テスト用の関数を 1つずつ ztest_unit_test() というマクロに渡します。
最終的に test_sprintf は struct unit_test の配列になり、配列を ztest_run_test_suite() に渡すと最初のテストから順番に実行してくれるという仕組みです。ztest の使い方は主題ではないのでこのくらいにしておきます。
// zephyr/tests/lib/sprint/src/main.c
void test_main(void)
{
ztest_test_suite(test_sprintf, //★変数名★
ztest_unit_test(test_sprintf_double), //★テスト 1つ目★
ztest_unit_test(test_sprintf_integer), //★テスト 2つ目★
ztest_unit_test(test_vsprintf),
ztest_unit_test(test_vsnprintf),
ztest_unit_test(test_sprintf_string),
ztest_unit_test(test_sprintf_misc));
ztest_run_test_suite(test_sprintf);
}
今回は ztest がテスト実行時に何を行うのかを調べ、浮動小数点数命令で例外が発生する原因を探します。まずは前半の ztest_test_suite() マクロから。
// zephyr/subsys/testsuite/ztest/include/ztest_test.h
/**
* @brief Define a test suite
*
* This function should be called in the following fashion:.c
* ztest_test_suite(test_suite_name,
* ztest_unit_test(test_function),
* ztest_unit_test(test_other_function)
* );
*
* ztest_run_test_suite(test_suite_name);
* ```
*
* @param suite Name of the testing suite
*/
#define ztest_test_suite(suite, ...) \
static ZTEST_DMEM struct unit_test _##suite[] = { \
__VA_ARGS__, { 0 } \
}
struct unit_test {
const char *name;
void (*test)(void);
void (*setup)(void);
void (*teardown)(void);
uint32_t thread_options;
};
/**
* @brief Define a test function
*
* This should be called as an argument to ztest_test_suite.
*
* @param fn Test function
*/
#define ztest_unit_test(fn) \
ztest_unit_test_setup_teardown(fn, unit_test_noop, unit_test_noop)
/**
* @brief Define a test with setup and teardown functions
*
* This should be called as an argument to ztest_test_suite. The test will
* be run in the following order: @a setup, @a fn, @a teardown.
*
* @param fn Main test function
* @param setup Setup function
* @param teardown Teardown function
*/
#define ztest_unit_test_setup_teardown(fn, setup, teardown) { \
STRINGIFY(fn), fn, setup, teardown, 0 \
}
//// 展開例: ztest_unit_test(test_sprintf_double)
// fn = test_sprintf_double
ztest_unit_test_setup_teardown(test_sprintf_double, unit_test_noop, unit_test_noop)
// fn = test_sprintf_double, setup = unit_test_noop, teardown = unit_test_noop
{
name : STRINGIFY(test_sprintf_double),
test : test_sprintf_double,
setup : unit_test_noop,
teardown : unit_test_noop,
thread_options: 0
}
最初にちょこっと書いたとおり、ztest_test_suite() は struct unit_test の配列宣言に展開され、ztest_unit_test() は配列の要素の初期化値に展開されます。
構造体 unit_test に thread_options というメンバーがいます。このメンバーが浮動小数点数命令のサポートに重要な役割を果たします。ztest_unit_test() だと、常に thread_options は 0 になる、という点だけ覚えておいてくれれば OK です。
GitHub を見ていたら Zephyr の Member に招待されていました。メーリングリストに来ていたメール(メールのアーカイブへのリンク)によると、現状 MAINTAINERS.yml に記載されている Collaborator を全員招待したらしいです。
Member といっても Issue のタグや担当者を付け外しできるようになる程度で、これといった権限はありません。
合計:
本日:
< | 2020 | > | ||||
<< | < | 12 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | - | - |
管理者: Katsuhiro Suzuki(katsuhiro( a t )katsuster.net)
This is Simple Diary 1.0
Copyright(C) Katsuhiro Suzuki 2006-2016.
Powered by PHP 5.2.17.
using GD bundled (2.0.34 compatible)(png support.)